Make a clean environment
rm(list=ls())
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Load packages
packages.list <- c("ggplot2","treeio","ggtree","ggnewscale","ape","dplyr","tidyverse","tidyr","phytools","RColorBrewer","lubridate","readxl","ggforce","ggstance","ggridges","cowplot","hexbin","scales","haven","network","ggnetwork","intergraph","igraph","ggraph","graphlayouts","scatterpie","maps","mapdata","maptools","rgdal","rgeos","broom","ggrepel","ggridges","magick","ggbeeswarm","ggrastr", "extrafont","svglite")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#"plyr","Cairo","ggmap","emojifont","rPinecone","pairsnp","CoordinateCleaner","gridExtra","dendextend","ggdendro",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#BiocManager::install("ggtree")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#BiocManager::install("treeio")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
for(pkg in packages.list){
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
eval(bquote(library(.(pkg)))) }
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Confirm current environmental setup
R.Version()
$platform
[1] "x86_64-apple-darwin20"
$arch
[1] "x86_64"
$os
[1] "darwin20"
$system
[1] "x86_64, darwin20"
$status
[1] ""
$major
[1] "4"
$minor
[1] "3.0"
$year
[1] "2023"
$month
[1] "04"
$day
[1] "21"
$`svn rev`
[1] "84292"
$language
[1] "R"
$version.string
[1] "R version 4.3.0 (2023-04-21)"
$nickname
[1] "Already Tomorrow"
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
print(sessionInfo())
R version 4.3.0 (2023-04-21)
Platform: x86_64-apple-darwin20 (64-bit)
Running under: macOS Monterey 12.6.1
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.11.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: Europe/London
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] sf_1.0-13 svglite_2.1.1 extrafont_0.19 ggrastr_1.0.2
[5] magick_2.7.4 ggdendro_0.1.23 dendextend_1.17.1 graphlayouts_1.0.0
[9] ggraph_2.1.0 pairsnp_0.1.0 haven_2.5.2 scales_1.2.1
[13] emojifont_0.5.5 hexbin_1.28.3 gridExtra_2.3 CoordinateCleaner_2.0-20
[17] Cairo_1.6-0 randomcoloR_1.1.0.1 ggrepel_0.9.3 broom_1.0.5
[21] rgeos_0.6-3 rgdal_1.6-7 maptools_1.1-7 sp_1.6-1
[25] mapdata_2.3.1 scatterpie_0.2.1 ggridges_0.5.4 intergraph_2.0-2
[29] igraph_1.5.0 ggnetwork_0.5.12 network_1.18.1 RColorBrewer_1.1-3
[33] rPinecone_0.1.0 devtools_2.4.5 usethis_2.2.0 ggnewscale_0.4.9
[37] treeio_1.24.1 plyr_1.8.8 readxl_1.4.2 phytools_1.5-1
[41] maps_3.4.1 ape_5.7-1 ggbeeswarm_0.7.2 ggstance_0.3.6
[45] ggforce_0.4.1 cowplot_1.1.1 ggtree_3.8.0 lubridate_1.9.2
[49] forcats_1.0.0 stringr_1.5.0 dplyr_1.1.2 purrr_1.0.1
[53] readr_2.1.4 tidyr_1.3.0 tibble_3.2.1 ggplot2_3.4.2
[57] tidyverse_2.0.0
loaded via a namespace (and not attached):
[1] subplex_1.8 fs_1.6.2 bitops_1.0-7 oai_0.4.0
[5] httr_1.4.6 doParallel_1.0.17 rgbif_3.7.7 numDeriv_2016.8-1.1
[9] profvis_0.3.8 tools_4.3.0 backports_1.4.1 utf8_1.2.3
[13] R6_2.5.1 lazyeval_0.2.2 urlchecker_1.0.1 withr_2.5.0
[17] prettyunits_1.1.1 cli_3.6.1 textshaping_0.3.6 labeling_0.4.2
[21] sass_0.4.6 mvtnorm_1.2-2 proxy_0.4-27 systemfonts_1.0.4
[25] yulab.utils_0.0.6 foreign_0.8-84 showtext_0.9-6 sessioninfo_1.2.2
[29] geiger_2.0.11 plotrix_3.8-2 rstudioapi_0.14 sysfonts_0.8.8
[33] optimParallel_1.0-2 generics_0.1.3 gridGraphics_0.5-1 combinat_0.0-8
[37] gtools_3.9.4 Matrix_1.5-4 fansi_1.0.4 S4Vectors_0.38.1
[41] clipr_0.8.0 terra_1.7-39 lifecycle_1.0.3 whisker_0.4.1
[45] scatterplot3d_0.3-44 yaml_2.3.7 clusterGeneration_1.3.7 gplots_3.1.3
[49] Rtsne_0.16 grid_4.3.0 promises_1.2.0.1 crayon_1.5.2
[53] miniUI_0.1.1.1 lattice_0.21-8 pillar_1.9.0 knitr_1.43
[57] codetools_0.2-19 fastmatch_1.1-3 glue_1.6.2 V8_4.3.0
[61] ggfun_0.0.9 data.table_1.14.8 remotes_2.4.2 vctrs_0.6.3
[65] cellranger_1.1.0 gtable_0.3.3 cachem_1.0.8 xfun_0.39
[69] mime_0.12 tidygraph_1.2.3 rnaturalearth_0.3.3 coda_0.19-4
[73] iterators_1.0.14 showtextdb_3.0 units_0.8-2 ellipsis_0.3.2
[77] nlme_3.1-162 bslib_0.4.2 vipor_0.4.5 KernSmooth_2.23-20
[81] colorspace_2.1-0 BiocGenerics_0.46.0 DBI_1.1.3 raster_3.6-20
[85] phangorn_2.11.1 mnormt_2.1.1 tidyselect_1.2.0 processx_3.8.1
[89] extrafontdb_1.0 compiler_4.3.0 curl_5.0.1 expm_0.999-7
[93] xml2_1.3.4 caTools_1.18.2 classInt_0.4-9 quadprog_1.5-8
[97] callr_3.7.3 digest_0.6.33 rmarkdown_2.22 htmltools_0.5.5
[101] pkgconfig_2.0.3 fastmap_1.1.1 rlang_1.1.1 htmlwidgets_1.6.2
[105] shiny_1.7.4 farver_2.1.1 jquerylib_0.1.4 jsonlite_1.8.5
[109] statnet.common_4.9.0 magrittr_2.0.3 ggplotify_0.1.0 patchwork_1.1.2
[113] geosphere_1.5-18 munsell_0.5.0 Rcpp_1.0.11 viridis_0.6.3
[117] proto_1.0.0 stringi_1.7.12 MASS_7.3-58.4 pkgbuild_1.4.0
[121] parallel_4.3.0 hms_1.1.3 ps_1.7.5 reshape2_1.4.4
[125] stats4_4.3.0 pkgload_1.3.2 evaluate_0.21 deSolve_1.35
[129] tzdb_0.4.0 foreach_1.5.2 tweenr_2.0.2 httpuv_1.6.11
[133] Rttf2pt1_1.3.12 polyclip_1.10-4 xtable_1.8-4 e1071_1.7-13
[137] tidytree_0.4.2 later_1.3.1 viridisLite_0.4.2 class_7.3-21
[141] ragg_1.2.5 aplot_0.1.10 memoise_2.0.1 beeswarm_0.4.0
[145] IRanges_2.34.0 cluster_2.1.4 timechange_0.2.0
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Make some shortcuts for plotting
y.theme.strip <- theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y= element_blank())
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
y.theme.strip.partial <- theme(axis.text.y = element_blank(), axis.ticks.y= element_blank())
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
x.theme.strip <- theme(axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x= element_blank())
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
x.theme.strip.partial <- theme(axis.text.x = element_blank(), axis.ticks.x= element_blank())
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
x.theme.strip.labs <- theme(axis.text.x = element_blank(),axis.title.x = element_blank())
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
x.theme.axis.rotate <- theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
legend.strip <- theme(legend.position = "none")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
theme.text.size <- theme(text = element_text(size = 10))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
'%notin%' <- Negate('%in%')
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
max.font.size <- 7
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
basic.font.size <- 6
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
min.font.size <- 5.25
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
theme.text.size <- theme(text = element_text(size = basic.font.size))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
theme.text.size.within <- (5/14)*min.font.size
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
panel.lab.size <- 10
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Specify raw data - global dataset
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Data/"
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/data/"
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Data_input_directory <- paste0(getwd(), "/inputdata/")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
################################
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#### Tree data
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# ML tree (refined dataset)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.MLtree.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.fix-zero-dist.treefile")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Pyjar tree (refined dataset)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.pyjar.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.pyjar.tre")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Full size BEAST2 analysis - previously generated as part of Beale, 2021.
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
full.beast2.tree.file <- paste0(Data_input_directory,"TPA-uber_beast2_strict-skyline-500M_10pop_consensus.tree")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Ancestral reconstruction of global TPA ML tree from TreeTime (refined dataset)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.treetime.ancestral.tree.file <- paste0(Data_input_directory,"TPA.annotated_tree.fix-hung.nexus")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.treetime.ancestral.vcf.file <- paste0(Data_input_directory,"TPA-uber.midpoint.ancestral_sequences.fix-hung.vcf")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Functionally annotated variants, extracted from snpEff vcf into tsv using snpSift
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.snpEff.file <- paste0(Data_input_directory,"TPA-uber.midpoint.ancestral_sequences.relab.bcf.ann.vcf.vartab.sepline.tsv")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Gff file for SS14 reference genome, containing gene positions/annotations
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
SS14.gff.file <- paste0(Data_input_directory,"Treponema_pallidum_subs._pallidum_SS14.NC_021508.1.2021-06-13.gff")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
################################
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#### Meta data
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Supplement from TPA-Uber paper - Beale, 2021
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.meta2.file <- paste0(Data_input_directory,"Sup_Data1_Global_Sample-Metadata__09-2022.xlsx")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# England specific metadata collated by PHE/UKHSA
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked.file <- paste0(Data_input_directory,"Sup_Data2_TPA.UK-only.PHE.metadata.2022-02-02.xlsx")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# England specific mapping shapefile data with Public Health Boundaries
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Imported datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
UK.publichealth.shapefile.data <- paste0(Data_input_directory,"Public_Health_England_Centres_(December_2016)_Boundaries")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
################################
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#### Externally plotted figures (e.g. GrapeTree) for inclusion in multipanel figures
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Externally plotted grapetree minimum spanning tree for whole of England - code to extract subtree that was used to make this is included later in this Rnotebook
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.UK.Grapetree.sublineages.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.sublineage-MSTree.Inkscaped.svg")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Externally plotted grapetree minimum spanning tree for whole of England - 3-variable plots
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.UK.Grapetree.3way.file <- paste0(Data_input_directory,"TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-3.svg")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Externally plotted grapetree minimum spanning tree for whole of England - HIV status
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.UK.Grapetree.HIV.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.HIVstatus-MSTree_inkscaped.svg")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Externally plotted grapetree minimum spanning tree for North East England networks
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Specify directory to output plots
Figure_output_directory <- paste0(getwd(), "/Figures_revision_03-2023/")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#"/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Figures/Figure_Drafting/Working_Figures_08-2022/"
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Read in trees
TPA.MLtree <- midpoint.root(read.tree(TPA.MLtree.file))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.pyjar.tree <- midpoint.root(read.tree(TPA.pyjar.file))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Read in final output metadata from Global Uber study (Beale 2021)
TPA.meta2.1 <- readxl::read_excel(TPA.meta2.file,sheet="Supplementary_Data1_Sample-Meta")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Create a colour scheme for Lineages, Countries and Continents
(consistent with Beale, 2021)
# Colouring for country
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
continental.country.cols.brew2 <- unique(TPA.meta2.1[,c("Geo_Country","Continent")])
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
continental.country.cols.brew2 <- continental.country.cols.brew2[order(continental.country.cols.brew2$Continent,continental.country.cols.brew2$Geo_Country),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
continental.country.cols.brew2$country.col <- c("#ec7014","#fec44f","#de2d26","#fb6a4a","#bdbdbd","#737373",brewer.pal(n=8,"Purples")[4:8],brewer.pal(n=8,"Blues")[3:8],brewer.pal(n=5,"Greens")[3:5],"#c51b8a","#8c510a")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Colouring for Continent
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
continental.cols.brew2 <- data.frame(Continent=sort(unique(TPA.meta2.1$Continent)),stringsAsFactors=F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
continental.cols.brew2$continent.col <- c("#fec44f","#de2d26","#bdbdbd","#2171b5","#74c476","#c51b8a","#ec7014")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Colouring for TPA Lineage
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA_Lineage.cols <- data.frame(Lineage=sort(unique(TPA.meta2.1$TPA_Lineage)),stringsAsFactors=F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA_Lineage.cols$Lineage.col <- c("royalblue2", "indianred1")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
#c("#436eee", "#666666","#ff6a6a")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA_Lineage.cols$Lineage <- factor(TPA_Lineage.cols$Lineage, levels=c("Nichols","SS14","outlier"))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Lineage Hexcodes
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# royalblue2 #436eee
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# indianred1 #ff6a6a
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Define colours for sublineages
# Define sublineage clustering scheme using brew colourscales
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- data.frame(unique(TPA.meta2.1[,c("TPA_Lineage","TPA.pinecone.sublineage")]), stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$TPA_Lineage,sublineages.cols.brew$TPA.pinecone.sublineage),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew$sublin.order <- as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))
Warning: NAs introduced by coercionError in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$sublin.order),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# For revised bootstrapped clusters
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew$sublineage.cols <- c("#FC9272","#EF3B2C",brewer.pal(n=4,"Greens")[2:4],brewer.pal(n=4,"YlOrBr")[c(2,3)],brewer.pal(n=6,"Blues")[2:6],brewer.pal(n=6,"Purples")[2:6],"grey80","grey80","grey80","grey80")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- unique(sublineages.cols.brew[,c("TPA.pinecone.sublineage","sublineage.cols")])
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- sublineages.cols.brew[order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))),]
Warning: NAs introduced by coercionError in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew$TPA.pinecone.sublineage <- factor(sublineages.cols.brew$TPA.pinecone.sublineage, levels=sublineages.cols.brew$TPA.pinecone.sublineage)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- sublineages.cols.brew[!is.na(sublineages.cols.brew$sublineage),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
colnames(sublineages.cols.brew) <- c("sublineage","sublineage.cols")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
sublineages.cols.brew <- unique(sublineages.cols.brew)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Restrict analysis to high quality genomes (and tree)
TPA.meta2.1 <- TPA.meta2.1[TPA.meta2.1$finescale.analysis=="Yes",]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Create a “UK” variable, and a “PHE” variable
TPA.meta2.1$is.UK <- ifelse(TPA.meta2.1$Geo_Country=="UK","UK","Other")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.meta2.1$is.PHE <- ifelse(TPA.meta2.1$Geo_Country=="UK" & grepl("PHE",TPA.meta2.1$Sample_Name),"PHE","Other")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare ML tree
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.MLtree.ggtree <- ggtree(TPA.MLtree,layout = "fan",open.angle = 10, right=T)
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare country dataset
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.countries.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Country=TPA.meta2.1$Geo_Country, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare continent dataset
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.continents.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Continent=TPA.meta2.1$Continent, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare UK data strip
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.UK.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, England=TPA.meta2.1$is.UK, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.UK.p[TPA.rawseq.UK.p$England=="UK",] <- "England"
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare PHE data strip
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.PHE.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, PHE=TPA.meta2.1$is.PHE, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare Major lineage dataset
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.Lineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Lineage=TPA.meta2.1$TPA_Lineage, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare sublineage lineage dataset
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.subLineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Prepare Year dataset (all samples)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.all.Years.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Year=TPA.meta2.1$Sample_Year, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
floor_5years <- function(value){ return(value - value %% 5) }
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.meta2.1$Sample_5year.window <- paste0(floor_5years(as.numeric(TPA.meta2.1$Sample_Year)),"-",floor_5years(as.numeric(TPA.meta2.1$Sample_Year))+5)
Warning: NAs introduced by coercionWarning: NAs introduced by coercionError in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Some samples have uncertain dates (up to 20-30 years uncertainty), but for the purposes of these plotting categories we'll use the centrepoint year
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.meta2.1$Sample_5year.window <- sapply(1:nrow(TPA.meta2.1), function(x) ifelse(TPA.meta2.1$Sample_Year[x]=="-",NA, ifelse(is.na(TPA.meta2.1$Sample_5year.window[x]),NA, ifelse(TPA.meta2.1$Sample_Year[x]=="1950-1980","1965-1970",ifelse(TPA.meta2.1$Sample_Year[x]=="1960-1980","1965-1970" ,ifelse(TPA.meta2.1$Sample_Year[x]=="1980-1999","1985-1990",TPA.meta2.1$Sample_5year.window[x]))))))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.meta2.1$Sample_year.1990.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1990,TPA.meta2.1$Sample_Year,"<1990")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.meta2.1$Sample_year.1999.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1999,TPA.meta2.1$Sample_Year,"<1999")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
TPA.rawseq.year.cuttoff.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sample.Year=TPA.meta2.1$Sample_year.1999.cuttoff, stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Bring in PHE metadata
PHE.metadata.linked <- readxl::read_excel(PHE.metadata.linked.file)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Do some cleanup and factoring of variables
PHE.metadata.linked$age_group <- factor(PHE.metadata.linked$age_group, levels=rev(c("16-24","25-34","35-44","45+","Unknown")))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked$london <- factor(PHE.metadata.linked$london,levels=rev(c("Yes","No","Unknown")))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked$ukborn <- factor(PHE.metadata.linked$ukborn,levels=rev(c("Yes","No","Unknown")))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked$hivpos <- factor(PHE.metadata.linked$hivpos, levels=rev(c("Yes","No","Unknown")))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# need to update terminology of 'MSM' to 'GBMSM'
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked[PHE.metadata.linked$gender_orientation=="MSM","gender_orientation"] <- "GBMSM"
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked$gender_orientation <- factor(PHE.metadata.linked$gender_orientation, levels=rev(c("MSW","GBMSM","WSM","MUnknown","Unknown")))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(c("East Midlands", "East of England", "London", "North East", "North West", "South East", "South West", "West Midlands", "Yorkshire and Humber", "UK (not England)", "Not Known")))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=sublineages.cols.brew$sublineage)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
### Extract information about duplicates
PHE.metadata.duplicates <- PHE.metadata.linked[!is.na(PHE.metadata.linked$dup_flag),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.duplicates <- PHE.metadata.duplicates[!is.na(PHE.metadata.duplicates$Sample_Name),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.patient.matches <- data.frame(
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
stringsAsFactors = FALSE,
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
dup_flag = c("1A","1B",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"2A","2B","3A","3B","4A",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"4B","5A","5B"),
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
dup_Patient = c("Patient 1",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"Patient 1","Patient 2",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"Patient 2","Patient 3","Patient 3",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"Patient 4","Patient 4",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"Patient 5","Patient 5"),
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
dup_Patient_Sample = c("sample 1",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"sample 2","sample 1",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"sample 2","sample 1","sample 2",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"sample 1","sample 2","sample 1",
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
"sample 2")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.duplicates <- left_join(PHE.metadata.duplicates, PHE.patient.matches, by="dup_flag")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.duplicates
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Duplicate Samples missing metadata are all ‘new duplicates’ and were
excluded due to low mapping coverage (all checked).
Samples labelled ‘ZA’ and ‘XB’ had duplicates in the original dataset,
but the reciprocal pairs were excluded due to quality isues.
Available pairs - Patient 3, Patient 4
PHE.metadata.duplicates.paired <- PHE.metadata.duplicates[PHE.metadata.duplicates$dup_Patient %in% c("Patient 3","Patient 4"),]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.duplicates.paired[order(PHE.metadata.duplicates.paired$dup_Patient, PHE.metadata.duplicates.paired$year,PHE.metadata.duplicates.paired$month),c("Sample_Name","dup_Patient", "month.fix", "year")]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
These will be revisited later in the analysis.
Patient 4 HIV-ve MSM (45+), UK born, PHE region D 2 samples, collected
in the same month and year Both samples are sublineage 1, and identical
(0 pwSNPs) Likely the same infection (depending on dates, treatment,
etc), but can’t rule out reinfection with same strain.
Patient 3 HIV-ve MSM (35-44), not UK born, based in London (C) 2
samples, collected 9 months apart Both samples are sublineage 1, but
have 7 pairwise SNPs between them (loads!) Reinfection – probably from a
different transmission network
However, based on the sample dates, as well as the outcome of the
downstream genetic analysis, we can see that Patient 3 has duplicate
infection events (different dates, 10 months apart) and the genomes are
distinct (7 SNPs apart), whereas Patient 4 samples were collected in the
same month and year (i.e. are likely duplicates from the same infection)
and has identical genomes.
For downstream analysis purposes, we will retain both samples for
Patient 3 (discrete infections), but exclude one sample from Patient 4
(duplicate infection samples) - ‘PHE150126A’ has much better genome
coverage, so exclude ‘PHE150125A’
### Further Exclusions
PHE130056A - duplicate of PHE130057B (already removed, so not relevant)
- don’t exclude! PHE170402A - quality control sample PHE170378A -
quality control sample
Exclude duplicate sequences
duplicate.exclusion.list <- c("PHE150125A","PHE170402A","PHE170378A")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.metadata.linked <- PHE.metadata.linked[PHE.metadata.linked$Sample_Name %notin% duplicate.exclusion.list,]
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Moving on…
Define some colour schemes
# define some colors for each region
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.region.cols.brew <- data.frame(UKHSA.region=c("North East", "North West", "Yorkshire and Humber", "East Midlands", "West Midlands", "East of England", "London", "South East","South West","UK (not England)", "Not Known"), stringsAsFactors=F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.region.cols.brew$region.col <- c("#A6CEE3","#1F78B4","#CAB2D6","#33A02C","#B2DF8A","#FF7F00","#E31A1C","#FB9A99","#D4BB02","grey75","grey25")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# HIV color scheme
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.hiv.cols <- data.frame(hivpos=rev(sort(unique(PHE.metadata.linked$hivpos))), stringsAsFactors=F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.hiv.cols$hiv.cols <- c("#1f78b4","#b2df8a","grey75")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Orientation colour scheme
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.orientation.cols <- data.frame(orientation=rev(sort(unique(PHE.metadata.linked$gender_orientation))), stringsAsFactors=F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.orientation.cols$orientation <- factor(PHE.orientation.cols$orientation, levels=rev(sort(unique(PHE.metadata.linked$gender_orientation))), labels=c("MSW","GBMSM","WSM","MUnknown","Unknown"))
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.orientation.cols$orientation.cols <- c("#1f78b4","#b2df8a","#fb9a99","#a6cee3","grey75")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# UK born colour scheme
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.ukborn.cols <- data.frame(ukborn=rev(sort(unique(PHE.metadata.linked$ukborn))),ukborn.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# London based colour scheme
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.london.cols <- data.frame(london=rev(sort(unique(PHE.metadata.linked$london))),london.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
# Age group colour scheme
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
PHE.Age.cols <- data.frame(age_group=rev(sort(unique(PHE.metadata.linked$age_group))),stringsAsFactors = T)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in assign(cacheKey, frame, .rs.CachedDataEnv) :
attempt to use zero-length variable name
PHE.Age.cols$age_group.cols <- c(brewer.pal(n=4,"YlGnBu"),"grey75")
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in assign(cacheKey, frame, .rs.CachedDataEnv) :
attempt to use zero-length variable name
# Sample Date colour scheme
PHE.year.cols <- data.frame(year=(sort(unique(PHE.metadata.linked$year))),stringsAsFactors = T)
PHE.year.cols$year.cols <- brewer.pal(n=7,"YlOrRd")
# Sample Date (all global data, but with 1990 cuttoff)
TPA.year.cuttoff.cols <- data.frame(date.cuttoff=c("<1999",1999:2019), date.cuttoff.col=c("#F2F2F2",colorRampPalette(brewer.pal(7, "YlOrRd"))(length(1999:2019))))
##### ## First describe the sequenced population as a whole
Set order of PHE regions
PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(PHE.region.cols.brew$UKHSA.region))
Generate some basic statistics about geographical PHE regions
(anonymised)
Make some plots
# Make hbar plot of sample counts by region
p.all.hbarplot <- ggplot(PHE.count.all, aes(x=count.per.region,y="")) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(values="grey30") +
geom_text(data=PHE.count.all, aes((count.per.region+12), "",label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
labs(y="All", x="Sample Count") +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(nrow=4))
#p.all.hbarplot
# make temporal bubbleplot of counts by region
p.all.year.bubbleplot <- ggplot(PHE.count.years, aes(as.numeric(year), y="All")) +
geom_point(alpha=0.65, aes(size=count.per.year)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2)) +
theme_light() +
scale_fill_manual(values="grey30") +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
labs(y="", x="Sample Year", size="Count")
#p.all.year.bubbleplot
# Make proportional hbar plot of HIV status
p.all.hiv.hbarplot <- ggplot(PHE.HIV.counts, aes(Count,y="",fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="All", x="HIV +ve") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.HIV.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
#p.all.hiv.hbarplot
p.all.orientation.hbarplot <- ggplot(PHE.orientation.counts, aes(Count,y="",fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="All", x="Orientation") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.orientation.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.orientation.hbarplot
p.all.ukborn.hbarplot <- ggplot(PHE.UKborn.counts, aes(Count,y="",fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="UK\nBorn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="All", x="UK Born") +
#guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.UKborn.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.ukborn.hbarplot
p.all.London.hbarplot <- ggplot(PHE.London.counts, aes(Count,y="",fill=london)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
labs(y="All", x="London") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.London.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.London.hbarplot
p.all.Age.hbarplot <- ggplot(PHE.Age.counts, aes(Count,y="",fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="All", x="Age Group") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.Age.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.Age.hbarplot
Plot combined plot for ‘all samples’
PHE.all.combiplot.1 <- plot_grid(p.all.year.bubbleplot, p.all.hbarplot + y.theme.strip, p.all.orientation.hbarplot + y.theme.strip, p.all.hiv.hbarplot + y.theme.strip, p.all.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.all.combiplot.1

Next just describe population distributions by PHE region
# generate some basic statistics about geographical PHE regions (anonymised)
PHE.geo.count <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre) %>%
dplyr::summarise(count.per.region=n()) %>%
dplyr::mutate(total.count=sum(count.per.region),fraction=count.per.region/total.count)
PHE.geo.count.years <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,year) %>%
dplyr::summarise(count.per.region.year=n())
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,year,TPA_Lineage) %>%
dplyr::summarise(count.per.region.year=n()) %>%
dplyr::mutate(total.count.year=sum(count.per.region.year)) %>%
dplyr::ungroup() %>%
tidyr::pivot_wider(names_from=TPA_Lineage, values_from = count.per.region.year)
`summarise()` has grouped output by 'phe_centre', 'year'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage[is.na(PHE.geo.count.years.lineage)] <- 0
PHE.geo.count.years.lineage$year <- as.numeric(PHE.geo.count.years.lineage$year)
# Generate some stats about HIV status
PHE.geo.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,hivpos) %>%
dplyr::summarise(count.per.region.hiv=n()) %>%
dplyr::mutate(total.region=sum(count.per.region.hiv)) %>%
dplyr::mutate(fraction=count.per.region.hiv/total.region) %>%
dplyr::arrange(desc(hivpos), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Double Check HIV status data for non-PHE dataset - confirmed no HIV+ves from non-MSM.
PHE.sourcelab.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(is.PHE, gender_orientation, hivpos) %>%
dplyr::summarise(count.per.orientation.hiv=n()) #%>%
`summarise()` has grouped output by 'is.PHE', 'gender_orientation'. You can override using the `.groups` argument.
#dplyr::filter(is.PHE!="PHE")
# Get total population stats for HIV
PHE.all.HIV.counts <- PHE.metadata.linked %>%
dplyr::group_by(hivpos) %>%
dplyr::summarise(count.hiv=n()) %>%
dplyr::mutate(count.total=sum(count.hiv), fraction=count.hiv/count.total)
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(gender_orientation) %>%
dplyr::summarise(orientation.count=n()) %>%
dplyr::mutate(orientation.percent=(orientation.count/sum(orientation.count)*100))
PHE.geo.orientation.counts <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre,gender_orientation) %>%
dplyr::summarise(count.per.region.orientation=n()) %>%
dplyr::mutate(total.region=sum(count.per.region.orientation)) %>%
dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
dplyr::mutate(fraction=count.per.region.orientation/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(orientation.percent=(count.per.region.orientation/sum(count.per.region.orientation)*100))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.geo.UKborn <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, ukborn) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(ukborn), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about London based
PHE.geo.London <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, london) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(london), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.geo.Age <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, age_group) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(age_group), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Lineage group
PHE.geo.Lineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, TPA_Lineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about sublineage group
PHE.geo.sublineage <- PHE.metadata.linked %>%
dplyr::group_by(phe_centre, TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.region=sum(Count)) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
Make some plots
# Make hbar plot of sample counts by region
p.region.hbarplot <- ggplot(PHE.geo.count, aes(count.per.region,phe_centre, fill=phe_centre)) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
geom_text(data=PHE.geo.count, aes((count.per.region+12), phe_centre,label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
labs(y="UKHSA Region", x="Sample Count") +
#coord_cartesian(xlim=c(0,130)) +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(ncol=2))
#p.region.hbarplot
# make temporal bubbleplot of counts by region
p.region.year.bubbleplot <- ggplot(PHE.geo.count.years, aes(as.numeric(year), phe_centre, colour=phe_centre)) +
geom_point(alpha=0.65, aes(size=count.per.region.year)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
theme_light() +
scale_color_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
labs(y="UKHSA Region", x="Sample Year", size="Count")
#p.region.year.bubbleplot
# Or a barplot of lineage by year & PHE region?
p.region.year.bubbleplot.barplot.facet.lineage <- PHE.geo.count.years.lineage %>% tidyr::pivot_longer(c(SS14, Nichols), names_to="TPA_Lineage", values_to="Count") %>%
ggplot(aes(year, Count, fill=TPA_Lineage)) +
geom_bar(stat='identity', width=0.6) +
facet_grid(phe_centre~., scales='free') +
guides(size=guide_legend(nrow=2)) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y = element_text(color = "grey25", size=7, angle=0))
#p.region.year.bubbleplot.barplot.facet.lineage
# Make proportional hbar plot of HIV status
p.region.hiv.hbarplot <- ggplot(PHE.geo.HIV.counts, aes(count.per.region.hiv,phe_centre,fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="UKHSA Region", x="HIV +ve") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.HIV.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.hiv), size=theme.text.size.within, inherit.aes = F) +
NULL
#p.region.hiv.hbarplot
p.region.orientation.hbarplot <- ggplot(PHE.geo.orientation.counts, aes(count.per.region.orientation,phe_centre,fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="UKHSA Region", x="Orientation") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.geo.orientation.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.orientation), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot
p.region.ukborn.hbarplot <- ggplot(PHE.geo.UKborn, aes(Count,phe_centre,fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UK Born",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="UKHSA Region", x="UK Born") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.UKborn, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.ukborn.hbarplot
p.region.London.hbarplot <- ggplot(PHE.geo.London, aes(Count,phe_centre,fill=london)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
labs(y="UKHSA Region", x="London") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.geo.London, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.London.hbarplot
p.region.Age.hbarplot <- ggplot(PHE.geo.Age, aes(Count,phe_centre,fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="UKHSA Region", x="Age Group") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.geo.Age, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.Age.hbarplot
Combined plot
PHE.region.combiplot.1 <- plot_grid(p.region.year.bubbleplot, p.region.hbarplot + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip, p.region.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.region.combiplot.1

Regions as a complex multipanel plot
# legends
PHE.region.combiplot.1.legends <- plot_grid(get_legend(p.region.year.bubbleplot), get_legend(p.region.hbarplot + y.theme.strip), get_legend(p.region.orientation.hbarplot + y.theme.strip), get_legend(p.region.hiv.hbarplot + y.theme.strip), get_legend(p.region.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)
# Arrange plots vertically
p.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.region.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.region.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.region.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.region.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
# Combine the plots
p.region.hbar.combi.plus.all <- plot_grid(p.year.bubbleplot.combi, p.region.hbar.counts.combi, p.region.hbar.orientation.combi, p.region.hbar.hiv.combi, p.region.hbar.Age.combi, nrow=1, rel_widths=c(6,4,4,4,4), labels = c("A","B","C","D","E"), label_size=panel.lab.size, vjust=0.25)
# and add the legends on top
p.region.hbar.combi.plus.all.with.legends <- plot_grid(p.region.hbar.combi.plus.all, PHE.region.combiplot.1.legends, ncol=1, rel_heights=c(6,1), scale = 0.95)
p.region.hbar.combi.plus.all.with.legends

#ggsave(paste0(Figure_output_directory, "SupFig2_TPA-PHE_Sample-metadistros-by-phe_region+all-combi.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=240, height=135, device='pdf', dpi=1200)
Now lets look at some genetic data
### Make ML tree with sublineage tippoints
TPA.MLtree.ggtree.tippoint <- TPA.MLtree.ggtree %<+% data.frame(Sample_Name=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F) +
geom_tippoint(aes(color=Sublineage), size=0.5, alpha=0.5, show.legend = FALSE) +
scale_color_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
Add metadata
# Continent
p.TPA.MLtree.PHE <- gheatmap(TPA.MLtree.ggtree.tippoint,
TPA.rawseq.continents.p, color=NULL,width=0.075,offset=0.00000025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Continent",values=continental.cols.brew2$continent.col, breaks=continental.cols.brew2$Continent, guide = guide_legend(order = 1,ncol=2)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# is UK
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,
TPA.rawseq.UK.p, color=NULL,width=0.075,offset=0.00001025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="England/Other", values=c("black","grey95"), breaks=c("England","Other"), guide = guide_legend(order = 2,ncol=2)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Lineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,TPA.rawseq.Lineage.p, color=NULL,width=0.075,offset=0.00002025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Lineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage, guide = guide_legend(order = 3, ncol=2)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
NULL
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# sublineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F), color=NULL,width=0.075,offset=0.00003025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 4, ncol=3)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
NULL
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
plot
p.TPA.MLtree.PHE

#ggsave(paste0(Figure_output_directory, "SupFig3_TPA-PHE_Global_Phylo+UK-highlights.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=160, device='pdf', dpi=1200)
### Geographic distributions of Lineages and Sublineages What about
sublineages?
p.region.Lineage.hbarplot <- ggplot(PHE.geo.Lineage, aes(Count,phe_centre,fill=TPA_Lineage)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
labs(y="UKHSA Region", x="TPA Lineage") +
guides(fill=guide_legend(nrow=3)) +
#geom_text(data=PHE.geo.Lineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
p.region.sublineage.hbarplot <- ggplot(PHE.geo.sublineage, aes(Count,phe_centre,fill=TPA.pinecone.sublineage)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="UKHSA Region", x="TPA Sublineage") +
guides(fill=guide_legend(nrow=4)) +
#geom_text(data=PHE.geo.sublineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
NULL
Combi plot (geography lineages)
PHE.region.combiplot.2.lineages <- plot_grid(p.region.year.bubbleplot +legend.strip, p.region.hbarplot + y.theme.strip + legend.strip + coord_cartesian(xlim=c(0,150)), p.region.Lineage.hbarplot + y.theme.strip +legend.strip, p.region.sublineage.hbarplot + y.theme.strip +legend.strip, nrow=1, align="h", rel_widths=c(6,3,4,4), scale=0.99, labels=c("C","D","E","F"), label_size=panel.lab.size)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# separate out the plot for the legends
p.region.year.bubbleplot.legend <- get_legend(p.region.year.bubbleplot)
p.region.hbarplot.legend <- get_legend(p.region.hbarplot + y.theme.strip)
p.region.Lineage.hbarplot.legend <- get_legend(p.region.Lineage.hbarplot + y.theme.strip)
p.region.sublineage.hbarplot.legend <- get_legend(p.region.sublineage.hbarplot + y.theme.strip)
PHE.region.combiplot.2.lineages.legend <- plot_grid(p.region.year.bubbleplot.legend, p.region.hbarplot.legend, p.region.Lineage.hbarplot.legend, p.region.sublineage.hbarplot.legend, nrow=1, align="h", rel_widths=c(6,3,4,4))
PHE.region.combiplot.2.lineages <- plot_grid(PHE.region.combiplot.2.lineages, PHE.region.combiplot.2.lineages.legend, rel_heights = c(4,1), ncol=1)
PHE.region.combiplot.2.lineages

OK, let’s now add a map of these geographical distributions
Let’s used ONS published shape files - there is one available that shows
Public Health England region boundaries.
# Generate approximate regional GPS coords
PHE.region.GPS <- data.frame(
stringsAsFactors = FALSE,
phe_centre = c("East Midlands",
"East of England","London","North East","North West",
"South East","South West","West Midlands",
"Yorkshire and Humber","UK (not England)","Not Known"),
Longitude = c(-0.7,0.5,-0.2,-1.9,-2.4,
0.05,-2.9,-2,-0.8,0.1,0.63),
Latitude = c(52.9,52.4,51.5,55,53.7,
51.1,51,52.6,53.8,54.7,54.1)
)
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[4] <- "SS14"
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="Nichols",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[5] <- "Nichols"
PHE.region.GPS[is.na(PHE.region.GPS)] <- 0
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","total.region")], by="phe_centre")
colnames(PHE.region.GPS)[6] <- "Region_Count"
PHE.region.GPS$radius <- 0.5*(1-1/sqrt(PHE.region.GPS$Region_Count))
###############################
# Import datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.shapefile <- readOGR(dsn=UK.publichealth.shapefile.data)
Warning: OGR support is provided by the sf and terra packages among othersWarning: OGR support is provided by the sf and terra packages among othersWarning: OGR support is provided by the sf and terra packages among othersWarning: OGR support is provided by the sf and terra packages among othersWarning: OGR support is provided by the sf and terra packages among othersWarning: OGR support is provided by the sf and terra packages among othersWarning: OGR support is provided by the sf and terra packages among others
OGR data source with driver: ESRI Shapefile
Source: "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Github/Syphilis_Genomic_Epi_England_2022-23/inputdata/Public_Health_England_Centres_(December_2016)_Boundaries", layer: "Public_Health_England_Centres_(December_2016)_Boundaries"
with 9 features
It has 9 fields
#Reshape for ggplot2 using the Broom package
#UK.mapdata <- tidy(UK.shapefile, region="phec16nm")
UK.mapdata <- tidy(UK.shapefile)
Regions defined for each Polygons
UK.mapdata.codes <- data.frame(st_as_sf(UK.shapefile, group="phec16nm")) %>%
rownames_to_column("id") %>%
select(id, phec16nm)
UK.mapdata <- UK.mapdata %>% left_join(UK.mapdata.codes, by='id') %>%
mutate(id=phec16nm)
#UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color = "#FFFFFF", size = 0.25)
UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075)
#UK.gg <- UK.gg + coord_fixed(1) + theme_nothing()
#UK.gg
# Map plotting file becomes _very_ big - use ggrastr to reduce the size
UK.gg <-ggplot() + ggrastr::rasterise(geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075), dpi=400) + coord_fixed(1) + theme_nothing()
#rasterise(geom_point(aes(carat, price, colour = cut), data=diamonds), dpi=30)
# Convert UK regions to be compatible with map
# First find centre point for each region
UK.mapdata.regions.meancoords <- UK.mapdata %>% dplyr::group_by(id) %>%
dplyr::summarise(mean.lat=mean(lat), mean.long=median(long)) %>%
dplyr::ungroup()
colnames(UK.mapdata.regions.meancoords)[1] <- "phe_centre"
PHE.region.GPS.ukmap <- dplyr::left_join(PHE.region.GPS, UK.mapdata.regions.meancoords, by="phe_centre")
# Add artificial location for 'not known'
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.lat"] <- 600000
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.long"] <- 550000
# Shift "South East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="South East","mean.long"] <- 475000
# Shift "East of England East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="East of England","mean.lat"] <- 275000
# Not going to try plotting the 2 samples from elsewhere in the UK, so remove that row
PHE.region.GPS.ukmap <- PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre != "UK (not England)",]
# Create radius variable for plotting pie sizes (use log10(n)*20,000)
PHE.region.GPS.ukmap$radius.UK <- log10(PHE.region.GPS.ukmap$Region_Count)*20000
#PHE.geo.count.years.lineage
UK.gg.scatterpie <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap, aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("Nichols","SS14")) +
scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) + theme(legend.position="top")
UK.gg.scatterpie <- UK.gg.scatterpie + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)
UK.gg.scatterpie <- UK.gg.scatterpie + theme_nothing()
#? Add labels
UK.gg.scatterpie.labs <- UK.gg.scatterpie + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size = 0.1) + theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") +
theme.text.size +
theme_nothing()
UK.gg.scatterpie.labs

Now do an equivalent plot for sublineages
PHE.region.GPS.ukmap.sublin <- PHE.region.GPS.ukmap
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="1",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[11] <- "1"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="2",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[12] <- "2"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="3",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[13] <- "3"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="6",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[14] <- "6"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="8",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[15] <- "8"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[16] <- "14"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="15",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[17] <- "15"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="16",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[18] <- "16"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="Singleton",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[19] <- "Singleton"
PHE.region.GPS.ukmap.sublin[is.na(PHE.region.GPS.ukmap.sublin)] <- 0
# Most samples are either sublineage 1 or 14. Let's create a count of samples that are neither.
PHE.region.GPS.ukmap.sublin$`Other Sublineages` <- sapply(1:nrow(PHE.region.GPS.ukmap.sublin), function (x) PHE.region.GPS.ukmap.sublin$Region_Count[x]-sum(PHE.region.GPS.ukmap.sublin$`1`[x], PHE.region.GPS.ukmap.sublin$`14`[x]))
UK.gg.scatterpie.sublineage <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap.sublin[PHE.region.GPS.ukmap.sublin$mean.long!=0,], aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("1","14","Other Sublineages")) +
scale_fill_manual(name="TPA\nSublineage",values=c("#FC9272","#BCBDDC", "grey50"), breaks=c("1","14","Other Sublineages"))
# add legend
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)
#UK.gg.scatterpie <- UK.gg.scatterpie + x.theme.strip + y.theme.strip
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + theme_nothing()
#? Add labels
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size = 0.1) +
theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") +
theme.text.size +
theme_nothing()
UK.gg.scatterpie.sublineage

Combined map plot
UK.gg.scatterpie.combi <- plot_grid(UK.gg.scatterpie.labs, UK.gg.scatterpie.sublineage, ncol=2, labels = c("A","B"), label_size=panel.lab.size)
UK.gg.scatterpie.combi

Plot in combination with barplots
plot_grid(UK.gg.scatterpie.combi, PHE.region.combiplot.2.lineages, nrow=2, rel_heights=c(4,5))

#ggsave(paste0(Figure_output_directory,"Fig2_TPA-PHE_Map-Lineage+Barplots.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)
#ggsave(plot=plot_grid(UK.gg.scatterpie.combi, PHE.region.combiplot.2.lineages, nrow=2, rel_heights=c(4,5)), paste0(Figure_output_directory,"Fig2_TPA-PHE_Map-Lineage+Barplots.",format(Sys.Date(),"%Y%m%d"),".svg"), units='mm', width=190, height=185, device=svglite, dpi=1200)
### Analysis by sublineage
Now lets start exploring how samples are distributed by sublineage
Plot by sublineage
p.sublineage.year.bubbleplot <- ggplot(PHE.geo.sublin.years, aes(as.numeric(year), TPA.pinecone.sublineage, colour=TPA.pinecone.sublineage)) +
geom_point(alpha=0.65, aes(size=Count)) +
geom_line(alpha=0.25) +
guides(colour='none') +
scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) +
guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
theme_light() +
scale_color_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
labs(y="TPA Sublineage", x="Sample Year", size="Count")
#p.sublineage.year.bubbleplot
p.sublineage.hbarplot <- ggplot(PHE.sublin.count, aes(Count,TPA.pinecone.sublineage,fill=TPA.pinecone.sublineage)) +
geom_barh(stat="identity", position="stack", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="TPA Sublineage", x="Sample Count") +
geom_text(data=PHE.sublin.count, aes((Count+12), TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F) +
#coord_cartesian(xlim=c(0,200)) +
coord_cartesian(xlim=c(0,260)) +
guides(fill=guide_legend(ncol=2))
#p.sublineage.hbarplot
p.sublineage.orientation.hbarplot <- ggplot(PHE.sublineage.orientation.counts, aes(y=TPA.pinecone.sublineage,x=Count,fill=gender_orientation)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
labs(y="TPA Sublineage", x="Orientation") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.orientation.counts, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot
p.sublineage.hiv.hbarplot <- ggplot(PHE.sublineage.HIV, aes(y=TPA.pinecone.sublineage, x=Count,fill=hivpos)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
labs(y="TPA Sublineage", x="HIV +ve") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.HIV, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.hiv.hbarplot
p.sublineage.ukborn.hbarplot <- ggplot(PHE.sublineage.UKborn, aes(y=TPA.pinecone.sublineage,x=Count,fill=ukborn)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UK\nborn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
labs(y="TPA Sublineage", x="UK born") +
guides(fill=guide_legend(nrow=3)) +
geom_text(data=PHE.sublineage.UKborn, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.ukborn.hbarplot
p.sublineage.Age.hbarplot <- ggplot(PHE.sublineage.Age, aes(y=TPA.pinecone.sublineage, x=Count ,fill=age_group)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
labs(y="TPA Sublineage", x="Age Group") +
guides(fill=guide_legend(ncol=1)) +
geom_text(data=PHE.sublineage.Age, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.Age.hbarplot
p.sublineage.PHEregion.hbarplot <- ggplot(PHE.sublineage.PHEcentre, aes(y=TPA.pinecone.sublineage, x=Count, fill=phe_centre)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$PHE.region) +
labs(y="TPA Sublineage", x="UKHSA Region") +
guides(fill=guide_legend(nrow=4)) +
geom_text(data=PHE.sublineage.PHEcentre, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
Look at how sublineages are distributed by region
(sublineage-centric)
p.sublineage.PHEregion.hbarplot

Combine patient metadata into a plot
#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, p.sublineage.ukborn.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,2,2), scale=0.9)
#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,4), scale=0.9)
PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
PHE.sublineages.combiplot.1

Lets add the ‘all’ row again to the ‘by sublineage’ plot
# legends
PHE.sublineage.combiplot.1.legends <- plot_grid(get_legend(p.sublineage.year.bubbleplot), get_legend(p.sublineage.hbarplot + y.theme.strip), get_legend(p.sublineage.orientation.hbarplot + y.theme.strip), get_legend(p.sublineage.hiv.hbarplot + y.theme.strip), get_legend(p.sublineage.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)
# regions
#PHE.sublineage.combiplot.1.nolegend <- plot_grid(p.sublineage.year.bubbleplot + legend.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)
# Or do it vertically
p.sublineage.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.sublineage.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
p.sublineage.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))
# Combine the plots
p.sublineage.hbar.combi.plus.all <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,3,4,4,4), labels=c("A", "B", "C", "D", "E"),label_size=panel.lab.size, vjust=1, scale=0.99)
# and add the legends on top
#p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(PHE.sublineage.combiplot.1.legends, p.sublineage.hbar.combi.plus.all, ncol=1, rel_heights=c(1,9))
# legends below
p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(p.sublineage.hbar.combi.plus.all, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(8,1))
p.sublineage.hbar.combi.plus.all.with.legends

These patterns look fairly similar between sublineages, and (apart
from 1 & 14) the groups are very small. However, sublineage 14 does
appear to have a higher proportion of MSM compared to sublineage 1 and
others. Let’s test that formally using 2x2 fisher’s tests
PHE.MSM.counts.all <- PHE.metadata.linked %>%
dplyr::group_by(is.MSM, .drop=F) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange((is.MSM), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
PHE.sublineage.MSM.counts <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage,is.MSM, .drop=F) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange((is.MSM), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) #%>%
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
#dplyr::filter(!is.na(is.MSM))
PHE.sublineage.MSM.counts.wider <- PHE.sublineage.MSM.counts %>% dplyr::select(TPA.pinecone.sublineage, is.MSM, Count) %>%
tidyr::pivot_wider(names_from = is.MSM, values_from=Count) %>%
dplyr::mutate(MSM=replace_na(MSM, 0), Other=replace_na(Other, 0), Total=sum(MSM,Other)) %>%
#dplyr::select(-`NA`) %>%
dplyr::filter(Total!=0)
PHE.sublineage.MSM.pval <- data.frame(TPA.pinecone.sublineage=PHE.sublineage.MSM.counts.wider$TPA.pinecone.sublineage, p.fisher=sapply(1:nrow(PHE.sublineage.MSM.counts.wider), function (x) fisher.test(matrix(as.numeric(c(PHE.sublineage.MSM.counts.wider[x,"MSM"],
PHE.sublineage.MSM.counts.wider[x,"Other"],
PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="MSM","Count"], PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="Other","Count"])),nrow=2))[[1]]), stringsAsFactors=F)
PHE.sublineage.MSM.counts.wider <- dplyr::left_join(PHE.sublineage.MSM.counts.wider, PHE.sublineage.MSM.pval, by="TPA.pinecone.sublineage")
PHE.sublineage.MSM.counts.wider
### Visualisation of UK genomic relationships
Ok, let’s make a tree for displaying these relationships using the UK
dataset only
From some experimentation, a ‘GrapeTree’ minimum spanning network works
well for visualising the clonality of these populations. We can use a
SNP-scaled phylogeny as direct input to GrapeTree, and this will allow
branches to be scaled appropriately. However, although annotation is
allowed within the GrapeTree software, colours must be manually edited.
Final GrapeTree plots can then be imported back into R for combining
with other plots.
Alternative visualisations - grapetree?
Take the 526-global phylogeny (snp-scaled version from pyjar), and prune
to only include the UK strains from this study - this ensures the
topology is consistent accross studies.
TPA.pyjar.tree.subset.uk <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Sample_Name"])))
TPA.pyjar.tree.subset.global_beast_only.seqlanes <- TPA.meta2.1 %>% filter(full.temporal.analysis=='Yes') %>%
select(Cleaned_fastq_id) %>% pull()
TPA.pyjar.tree.subset.uk.seqlanes <- as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Cleaned_fastq_id"]))
ggtree(TPA.pyjar.tree.subset.uk)

#write.tree(TPA.pyjar.tree.subset.uk, paste0(Data_input_directory,"TPA.UK-only.pyjar.2022-02-03.tre"))
# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"
#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)
Tree independently visualised and annotated using GrapeTree.
Now import and integrate GrapeTree plot with metadata plots.
# Combine the plots
p.sublineage.hbar.combi.plus.all.B2F <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,4,4,4,4), labels=c("B", "C", "D", "E", "F"),label_size=panel.lab.size, vjust=1, scale=0.97)
# legends below
p.sublineage.hbar.combi.plus.all.with.legends.B2F <- plot_grid(p.sublineage.hbar.combi.plus.all.B2F, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(7,1))
#p.sublineage.hbar.combi.plus.all.with.legends.B2F
# Now bring in externally plotted Grapetree
p.TPA.UK.Grapetree.sublineages <- ggdraw() + draw_image(TPA.UK.Grapetree.sublineages.file)
p.TPA.UK.Grapetree.sublineages

p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree <- plot_grid(p.TPA.UK.Grapetree.sublineages, p.sublineage.hbar.combi.plus.all.with.legends.B2F, ncol=1, labels=c("A",""), label_size=panel.lab.size, rel_heights=c(3,5))
p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree

#ggsave(paste0(Figure_output_directory, "Fig1_TPA-PHE_Sample-distros-sublineage.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)
#ggsave(plot=p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree, paste0(Figure_output_directory, "Fig1_TPA-PHE_Sample-distros-sublineage.",format(Sys.Date(),"%Y%m%d"),".svg"), units='mm', width=190, height=185, device=svglite, dpi=1200)
Manage other GrapeTree plots (for consistency)
TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-2
# Bring in 3-way graphetree plot (3 different metadata variables using the same input tree)
TPA.UK.Grapetree.3way <- ggdraw() + draw_image(TPA.UK.Grapetree.3way.file)
TPA.UK.Grapetree.3way

#ggsave(paste0(Figure_output_directory, "SupFig4_TPA-PHE_Grapetree-3ways.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=145, height=180, device='pdf', dpi=1200)
And also do the HIV status plot
TPA.UK.Grapetree.HIV <- ggdraw() + draw_image(TPA.UK.Grapetree.HIV.file)
TPA.UK.Grapetree.HIV

#ggsave(paste0(Figure_output_directory, "SupFig5_TPA-PHE_Grapetree-HIV.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=110, device='pdf', dpi=1200)
### Phylogenetic context analyses
Ok, now lets look at some trees
First, let’s formalise BEAST tree plotting as three separate functions
to enable other trees to be plotted the same way
full.beast2.tree <- read.beast(full.beast2.tree.file)
full.beast2.tree@phylo$tip.label <- gsub("\\|.+$","",full.beast2.tree@phylo$tip.label, perl=T)
################################################################################################
# function to extract a tree based on sublineage
Extract_sublineage_tree_for_plot <- function(my.beast.tree, my.metadata, my.phe.meta, my.sublineage){
# get all tips to include from metadata, then calculate MRCA from tree
sublineage.test.mrca <- getMRCA(my.beast.tree@phylo, as.character(unlist(my.metadata[my.metadata$TPA.pinecone.sublineage==my.sublineage,"Sample_Name"])))
######
TPA.beast.subtree.test <- tree_subset(my.beast.tree, node=sublineage.test.mrca, levels_back=0)
return(TPA.beast.subtree.test)
}
#Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1)
################################################################################################
################################################################################################
# Function to prepare a beast tree with timescale indicators, posterior support and 95% HPD bars
plot_beast_subtree_with_HPD <- function(my.beast.tree, my.metadata, my.phe.meta, mrsd.fulltree){
# get MRCD for tree
mrsd.Beast.tree.test.s <- max(as.numeric(unlist(my.metadata[my.metadata$Sample_Name %in% my.beast.tree@phylo$tip.label,"Sample_Year"])))
mrsd.Beast.tree.test <- lubridate::ymd(paste0(mrsd.Beast.tree.test.s,"-06-01"))
mrsd.Beast.tree.fulltree <- lubridate::ymd(mrsd.fulltree)
#mrsd.Beast.tree.test
# plot basic tree
options(ignore.negative.edge=TRUE)
p.TPA.beast.subtree.test <- ggtree(my.beast.tree, mrsd=mrsd.Beast.tree.test, ladderize = T, size=0.4) + scale_x_continuous(breaks=seq(1960,2020,10), minor_breaks=seq(2000, 2020, 1)) +
theme_tree2() +
# Add date lines for easy interpretation
theme(panel.grid.major = element_line(color="grey50", size=.2),
panel.grid.minor = element_line(color="grey85", size=.2),
panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank())
# Add posterior support as node points
p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.8)),color="gray60",size=2,alpha=0.5, shape=18) +
geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.91)),color="gray40",size=3,shape=18,alpha=0.5) +
geom_point2(aes(subset=(!isTip & as.numeric(posterior)>=0.96)),color="black",size=3,shape=18,alpha=0.5)
######
# extract 95% HPD intervals - geom_range seems unable to do correctly with this tree (known bug for tip dated trees), so extract data and plot using geom_segment
TPA.beast.subtree.test.data <- fortify(my.beast.tree)
minmax <- t(matrix(unlist(TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"height_0.95_HPD"]),nrow=2))
bar_df <- data.frame(node_id=TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"node"],as.data.frame(minmax))
names(bar_df) <- c('node_id','min','max')
bar_df <- bar_df %>% filter(node_id > Ntip(my.beast.tree@phylo))
bar_df <- bar_df %>% left_join(TPA.beast.subtree.test.data, by=c('node_id'='node')) #%>% select(node_id,min,max,y)
#mrcd.decimal <- decimal_date(mrsd.Beast.tree.test)
mrcd.decimal <- decimal_date(mrsd.Beast.tree.fulltree)
# Now add HPDs to plot
p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_segment(aes(x=mrcd.decimal-max, y=y, xend=mrcd.decimal-min, yend=y), data=bar_df, color='red', alpha=0.2, size=2.0)
# Output tree
return(p.TPA.beast.subtree.test)
}
################################################################################################
################################################################################################
# Function to add metadata to tree
# Has two optional arguments "initial.track.offset" and "track.scaling" which can be used to alter the width and positioning of metadata tracks
plot_beast_subtree_with_PHE_metadata <- function(my.beast.tree.input, my.metadata, my.phe.meta, initial.track.offset, track.scaling){
# Add code to allow scaling up of the track offsets and widths - useful for much bigger length trees
if(missing(initial.track.offset)){
initial.track.offset <- 0
}
if(missing(track.scaling)){
track.scaling <- 1
}
# Calculate amount to offset each heatmap track
offset.dist <- 4*track.scaling
track.width <- (1/max(my.beast.tree.input$data$height)*3)*track.scaling
# make a list of taxa used in this plot
my.taxa.list <- as.character(unlist(filter(my.beast.tree.input$data, isTip==TRUE) %>% select(label)))
# make a color scale for sampling years
#PHE.sublintest.year.cols <- data.frame(year=sort(unique(as.numeric(unlist(my.metadata[(my.metadata$Sample_Name %in% my.taxa.list),"Sample_Year"],use.names=F)))),stringsAsFactors = T)
#PHE.sublintest.year.cols$year.cols <- colorRampPalette(brewer.pal(7, "YlOrRd"))(nrow(PHE.sublintest.year.cols))
# Or alternatively, use a common colour scheme for all data (maybe more sensible)
PHE.sublintest.year.cols <- data.frame(year=TPA.year.cuttoff.cols$date.cuttoff, year.cols=TPA.year.cuttoff.cols$date.cuttoff.col, stringsAsFactors = F)
# make metadata file for UK regions present in sublineage
sublin.test.region.meta <- data.frame(row.names=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"Sample_Name"])), Region=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"phe_centre"])), stringsAsFactors = F)
# Add heatmap strips
# Sample Year
#TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.all.Years.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
#scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
#ggnewscale::new_scale_fill()
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.year.cuttoff.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
ggnewscale::new_scale_fill()
# Add country
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, TPA.rawseq.countries.p, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*2),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="Country", values=continental.country.cols.brew2$country.col, breaks=continental.country.cols.brew2$Geo_Country, guide = guide_legend(order = 2)) +
ggnewscale::new_scale_fill()
# UK or non-UK
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional,
TPA.rawseq.UK.p, color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*3), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) +
scale_fill_manual(name="England/Other", breaks=c("England","Other"), values=c("black","grey95"), na.value = "white", guide = guide_legend(order = 3, ncol=2)) +
ggnewscale::new_scale_fill()
# UK PHE region
TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, sublin.test.region.meta, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*4),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="UKHSA Region", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, na.value = "white", guide = guide_legend(order = 4)) +
ggnewscale::new_scale_fill()
# TPA sublineage
#TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F), color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*5), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=2.5) +
#scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 5))
TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
new_scale_fill() +
geom_rootedge(2) +
NULL
# calculate number of taxa
test.taxacount <- length(my.taxa.list)
# Adjust final plot x and y axis to make space for labels using taxa counts
x.axis.limits <- ggplot_build(TPA.beast.subtree.test.global.plot1.regional)$layout$panel_scales_x[[1]]$range$range
TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional +
coord_cartesian(y=c(-0.5-(test.taxacount/15),test.taxacount+2), x=c(x.axis.limits[1],x.axis.limits[2]+3))
return(TPA.beast.subtree.test.global.plot1.regional)
}
################################################################################################
Great, now let’s plot a full beast tree
# function for x-axis time breaks needs tweaking for the full tree
TPA.Global.full.BeastTree.ukmeta <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(my.beast.tree = full.beast2.tree, my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, mrsd.fulltree = "2019-06-01") + scale_x_continuous(breaks=seq(1400,2020,50), minor_breaks=seq(1950, 2020, 5)), my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, track.scaling = 5)
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.Scale for x is already present.
Adding another scale for x, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
TPA.Global.full.BeastTree.ukmeta

#ggsave(paste0(Figure_output_directory,"SupFig7_TPA_FullBeastTree.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=240, device='pdf', dpi=1200)
Now do sublineage plots
Make some plots
# Sublineage 1
sublineage.1.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.2)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Sublineage.2
sublineage.2.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 2), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Sublineage.8
sublineage.8.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 8), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Sublineage.14
sublineage.14.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
Plot together?
Maybe with sublineage 1 expanded?
p.beast.trees.heatmap.sublineages.combi.offset1 <- plot_grid(sublineage.2.tree.heatmap,
sublineage.8.tree.heatmap,
sublineage.14.tree.heatmap,
ncol=2, labels=c("B - Sublineage 2","C - Sublineage 8","D - Sublineage 14"), label_size=panel.lab.size, scale=0.95, vjust=1.0)
p.beast.trees.heatmap.sublineages.combi.offset2 <- plot_grid(sublineage.1.tree.heatmap, p.beast.trees.heatmap.sublineages.combi.offset1, labels=c("A - Sublineage 1", ""), label_size=panel.lab.size, scale=0.975, ncol=2, rel_widths=c(6,11), vjust=2.5)
p.beast.trees.heatmap.sublineages.combi.offset2

#ggsave(paste0(Figure_output_directory,"SupFig8_TPA-PHE_Sublineage-BeastTrees.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=265, height=230, device='pdf', dpi=1200)
Need to explore sublineage 14 a bit more to get dates for those
subclades
sublineage.14.tree.heatmap + geom_tiplab(size=theme.text.size.within, linesize=0.4) #3

# Ok, there are multiple subclades in this tree
sublineage.14.tree.heatmap.data <- sublineage.14.tree.heatmap$data
# getMRCA(full.beast2.tree@phylo,c("PHE150150A","NL14","TPA_BCC122","TPA_BCC126","PHE140076A","TPA_UKBRG008")) 982
# full.beast2.tree@phylo$tip.label[phangorn::Descendants(full.beast2.tree@phylo, 982, type = c("tips"))[[1]]]
sublineage.14.lowerclade.list <- c("NL17", "NL19", "PHE140085A", "PHE140089A", "PHE150118A", "PHE150121A", "PHE150133A", "PHE150143A", "PHE150145A", "PHE150162A", "PHE150166A", "PHE150168A", "PHE160224A", "PHE160243A", "PHE160255A", "PHE160276A", "PHE160290A", "PHE160302A", "PHE160306A", "PHE170333A", "PHE170349A", "PHE170374A", "PHE170381A", "PHE170664A", "TPA_ESBCN005", "TPA_UKBIR032")
sublineage.14.upperclade.list <- c("NL14", "PHE140076A", "PHE150149A", "PHE150150A", "PHE150170A", "PHE160196A", "PHE160263A", "PHE160274A", "PHE160287A", "PHE160294A", "PHE160316A", "PHE160317A", "PHE170372A", "PHE170386A", "PHE170397A", "PHE170405A", "TPA_BCC081", "TPA_BCC088", "TPA_BCC089", "TPA_BCC101", "TPA_BCC122", "TPA_BCC126", "TPA_BCC136", "TPA_BCC169", "TPA_HUN180004", "TPA_HUN190020", "TPA_UKBIR044", "TPA_UKBRG007", "TPA_UKBRG008")
# Get MRCA date for lower clade
sublineage.14.lowerclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.lowerclade.list),"x"]
paste0("TMRCA for sublineage 14 lower clade: ",sublineage.14.lowerclade.list.tmrca)
[1] "TMRCA for sublineage 14 lower clade: 2006.53850498154"
# Get MRCA date for upper clade
sublineage.14.upperclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.upperclade.list),"x"]
paste0("TMRCA for sublineage 14 upper clade: ",sublineage.14.upperclade.list.tmrca)
[1] "TMRCA for sublineage 14 upper clade: 1999.15025243934"
Extract key information for sublineage 6 (two samples)
sublineage.6.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
sublineage.6.tree.heatmap.data <- sublineage.6.tree.heatmap$data
# Get MRCA date for upper clade
sublineage.6.beasttree.tmrca <- as.numeric(sublineage.6.tree.heatmap.data[sublineage.6.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6)@phylo, c("PHE130048A", "PHE160283A")),"branch"])
paste0("TMRCA for sublineage 6 upper clade: ",sublineage.6.beasttree.tmrca)
[1] "TMRCA for sublineage 6 upper clade: 1982.61865062176"
### Extract sample & population statistics from datasets for use in
manuscript text
Dataset and Geographical distributions
# dataset counts
paste0("Total UK samples in cleaned/deduplicated dataset: ",nrow(PHE.metadata.linked))
[1] "Total UK samples in cleaned/deduplicated dataset: 237"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="PHE",])," from PHE Ref lab at Colindale")
[1] "Of which: 195 from PHE Ref lab at Colindale"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="Other",])," from other labs")
[1] "Of which: 42 from other labs"
# proportion with geographical data
paste0("From UK samples, ", nrow(PHE.metadata.linked[(PHE.metadata.linked$phe_centre %notin% c("Not Known","UK (not England)")),])," were grouped into one of the 9 PH regions")
[1] "From UK samples, 217 were grouped into one of the 9 PH regions"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="UK (not England)",]), " were referred from outside England")
[1] "From UK samples, 2 were referred from outside England"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="Not Known",]), " had unknown region")
[1] "From UK samples, 18 had unknown region"
# counts & fractions by PHE region
PHE.geo.count
NA
Gender Orientation stats
PHE.orientation.counts
PHE.geo.orientation.counts
PHE.geo.HIV.counts
PHE.sublineage.orientation.counts
PHE.sublineage.Age
Sublineage Distributions
PHE.Lineage.count
PHE.sublin.count
PHE.geo.sublineage
Macrolide resistance stats
UK.macrolide.res <- PHE.metadata.linked %>%
dplyr::group_by(A2058G, A2059G) %>%
dplyr::summarise(Count.allele=n()) %>%
dplyr::ungroup() %>%
dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res
UK.macrolide.res.sublin <- PHE.metadata.linked %>%
dplyr::group_by(TPA.pinecone.sublineage, A2058G, A2059G) %>%
dplyr::summarise(Count.allele=n()) %>%
dplyr::ungroup() %>%
dplyr::group_by(TPA.pinecone.sublineage) %>%
dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res.sublin
# Calculate long form df, with different 23S alleles (A2058G, A2059G, WT, Uncertain) v.s. sublineage
UK.macrolide.res.sublin.long <- PHE.metadata.linked %>%
mutate(Resistance.allele=ifelse(A2058G=="Yes", "A2058G", ifelse(A2059G=="Yes", "A2059G", ifelse((A2058G=="No" & A2059G=="No"),"Wild Type", "Uncertain")))) %>%
dplyr::group_by(TPA.pinecone.sublineage, Resistance.allele) %>%
dplyr::summarise(Count.per.sublin.Macrolides=n()) %>%
dplyr::mutate(total.sublin=sum(Count.per.sublin.Macrolides),
fraction=Count.per.sublin.Macrolides/total.sublin) %>%
#dplyr::ungroup() %>%
dplyr::arrange((Resistance.allele), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Resistance.allele = factor(Resistance.allele, levels=rev(c("A2058G", "A2059G", "Uncertain", "Wild Type"))))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Make plot of macrolide resistance by sublineages
p.sublin.Macrolides.hbarplot <- ggplot(UK.macrolide.res.sublin.long, aes(Count.per.sublin.Macrolides, y=TPA.pinecone.sublineage, fill=Resistance.allele)) +
geom_barh(stat="identity", position="fill", width=0.65) +
theme_light() +
scale_fill_manual(name="Macrolide\nResistance\nAllele",values=c("indianred2", "steelblue1","grey55", "grey90"), breaks=c("A2058G", "A2059G", "Uncertain", "Wild Type")) +
labs(y="TPA Sublineage", x="Proportion with Macrolide Resistance Allele") +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
guides(fill=guide_legend(ncol=2)) +
geom_text(data=UK.macrolide.res.sublin.long, aes(cum_fract.mid, y=TPA.pinecone.sublineage,label=Count.per.sublin.Macrolides), size=theme.text.size.within, inherit.aes = F) +
NULL
p.sublin.Macrolides.hbarplot

# Combine plot with sublineage count bars
p.sublin.Macrolides.hbarplot.combi <- plot_grid(p.sublineage.hbarplot + guides(fill=guide_legend(ncol=3)), p.sublin.Macrolides.hbarplot + y.theme.strip, nrow=1, align=T, labels=c("A", "B"), label_size=panel.lab.size)
p.sublin.Macrolides.hbarplot.combi

#ggsave(paste0(Figure_output_directory,"SupFig9_TPA-PHE_Sublin-Macrolide-Res.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=160, height=120, device='pdf', dpi=1200)
Pairwise SNP analysis
OK, want to investigate the different patterns observable for the North
East of England (pale blue) in Sublineage 1
Multiple ways we can do this - including SNP distances (also multiple
ways to do that)
###
#Use phylogenetic distance from the SNP scaled tree
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist <- ape::cophenetic.phylo(TPA.pyjar.tree.subset.uk)
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt <- data.frame(Taxa1=row.names(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist), TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist, stringsAsFactors = F) %>% tidyr::gather(Taxa2, Distance.Phylo, -Taxa1)
# Taxa Comparisons label
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa_combination <- sapply(1:nrow(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt), function (x) paste0(sort(c(as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa1[x]),as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa2[x]))),collapse="___"))
# Merge together
#TPA.WGS.alignment.data.dist.melt <- dplyr::left_join(TPA.WGS.alignment.data.dist.melt, TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt[,c("Taxa_combination","Distance.Phylo")], by="Taxa_combination")
TPA.WGS.alignment.data.dist.melt <- TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt
TPA.WGS.alignment.data.dist.melt <- unique(TPA.WGS.alignment.data.dist.melt)
Ok, now bring in some metadata and comparisons
# Bring in and merge metadata
PHE.meta.pairwise.t1 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t1) <- paste0(colnames(PHE.meta.pairwise.t1),".t1")
colnames(PHE.meta.pairwise.t1)[1] <- "Taxa1"
PHE.meta.pairwise.t2 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t2) <- paste0(colnames(PHE.meta.pairwise.t2),".t2")
colnames(PHE.meta.pairwise.t2)[1] <- "Taxa2"
PHE.alignment.data.dist.melt.meta <- plyr::join(TPA.WGS.alignment.data.dist.melt,PHE.meta.pairwise.t1, by="Taxa1", type="left")
PHE.alignment.data.dist.melt.meta <- plyr::join(PHE.alignment.data.dist.melt.meta,PHE.meta.pairwise.t2, by="Taxa2", type="left")
# Exclude missing data (e.g. missing sublineage) - this will also remove non-UK samples, since full metadata is missing here
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1),]
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2),]
Define comparisons
# Same sample
PHE.alignment.data.dist.melt.meta$same.sample <- ifelse(PHE.alignment.data.dist.melt.meta$Taxa1==PHE.alignment.data.dist.melt.meta$Taxa2,"same", "different")
# Years between samples
PHE.alignment.data.dist.melt.meta$year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$year.t2))
PHE.alignment.data.dist.melt.meta$Sample_Year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t2))
# Years between decimal date (more precise temporal distance)
PHE.alignment.data.dist.melt.meta$decimal.date.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t2))
# Epidemiological time between - catagorical
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<1/12,"month", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3/12, "quarter", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6/12, "half year", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "1 year",ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "2 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "3 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "4 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "5 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "6 years",">6 years")))))))))
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- factor(PHE.alignment.data.dist.melt.meta$epi.time.distance.cat, levels=c("month", "quarter","half year","1 year", "2 years", "3 years", "4 years", "5 years", "6 years", ">6 years"))
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat.years <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "0", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "1", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "2", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "3", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "4", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "5",">5"))))))
# Same country
PHE.alignment.data.dist.melt.meta$same.country <- ifelse(PHE.alignment.data.dist.melt.meta$Geo_Country.t1 == PHE.alignment.data.dist.melt.meta$Geo_Country.t2, "same", "different")
# Is UK
PHE.alignment.data.dist.melt.meta$both.uk <- ifelse(PHE.alignment.data.dist.melt.meta$is.UK.t1 == PHE.alignment.data.dist.melt.meta$is.UK.t2, "same", "different")
# Is PHE
PHE.alignment.data.dist.melt.meta$both.PHE <- ifelse(PHE.alignment.data.dist.melt.meta$is.PHE.t1 == PHE.alignment.data.dist.melt.meta$is.PHE.t2, "same", "different")
# Same TPA Lineage (cleaned up classifications)
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- ifelse(PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1==PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse((PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1[x]=="0" | PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2[x]=="0"),NA,PHE.alignment.data.dist.melt.meta$same.TPA.Lineage[x]))
# Same TPA sublineage
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- ifelse(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1==PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2,"same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse(((PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1[x]=="Singleton") |(PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2[x]=="Singleton")),"different",PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster[x]))
# Define Genetic relationships hierarchically
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy <- ifelse(PHE.alignment.data.dist.melt.meta$Distance==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy.ph <- ifelse(PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))
# Same PHE region
PHE.alignment.data.dist.melt.meta$same.PHE.region <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1==PHE.alignment.data.dist.melt.meta$phe_centre.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$PHE.centre.combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t2[x]))),collapse="___"))
# does the combination included London?
PHE.alignment.data.dist.melt.meta$involves.London <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="London" | PHE.alignment.data.dist.melt.meta$phe_centre.t2=="London", "London", "not-London")
# Orientation pair
PHE.alignment.data.dist.melt.meta$Orientation_combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]))),collapse="___"))
#PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "MSM",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" | PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "Mixed",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSW" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="WSM","Heterosexual",
# ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="WSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSW","Heterosexual","Unknown")))))
PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="GBMSM", "GBMSM",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"),"Heterosexual",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"), "Mixed",
ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM", "Mixed", "Unknown")))))
# Country Comparisons label
PHE.alignment.data.dist.melt.meta$Country_combinations <- paste0(PHE.alignment.data.dist.melt.meta$Geo_Country.t1,"___",PHE.alignment.data.dist.melt.meta$Geo_Country.t2)
# Subset to PHE data only (effectively already done, but let's be explicit)
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same" & PHE.alignment.data.dist.melt.meta$both.PHE=="same"),]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[PHE.TPA.alignment.data.dist.melt.meta$PHE.only=="PHE",]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same"),]
# Make single sided
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[!duplicated(PHE.TPA.alignment.data.dist.melt.meta$Taxa_combination),]
### Perform a more detailed analysis of samples from the North East of
England
Do a more detailed exploration of the North East of England
PHE.metadata.linked2.region_NorthEast <- PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East",]
# Constrain by samples being from the North East
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="North East" & PHE.alignment.data.dist.melt.meta$same.sample=="different"),]
# Constrain by the same PHE region
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$same.PHE.region=="same",]
#Just plot these distros
p.NorthEast.Pairwise.SNPs.unconstrained <- ggplot(PHE.alignment.data.dist.melt.meta.NorthEast.clusters, aes(Distance.Phylo)) +
geom_histogram(binwidth = 1) +
theme_bw() +
theme.text.size +
labs(x="Pairwise SNP Distance", y="Comparison Count")
p.NorthEast.Pairwise.SNPs.unconstrained

Make a single linkage network from the North East samples
# Constrain by SNP distance (looser than previously - we just want to find basic groupings within sublineage 1 for NE samples)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo<=2,]
# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo),]
# cleanup some data noise
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$year.t1),]
# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat")]
############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[!duplicated(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename),]
# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2)
############
#inverse weight
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.inv <- 1/PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.Phylo
# Make actual network
set.seed(1235)
PHE.NorthEast.network <- network(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F)
PHE.NorthEast.network.gg <- ggnetwork(PHE.NorthEast.network, layout = "kamadakawai", weights = "Distance.inv")
PHE.NorthEast.network.gg$Taxa1 <- PHE.NorthEast.network.gg$vertex.names
# extract temporal clusters from network
PHE.NorthEast.network.ig <- asIgraph(PHE.NorthEast.network)
PHE.NorthEast.network.components <- data.frame(Taxa1=network.vertex.names(PHE.NorthEast.network), vertex.no=as.vector(V(PHE.NorthEast.network.ig)), cluster=igraph::components(PHE.NorthEast.network.ig)$membership)
# For ease of story telling in the paper, flip clusters 2 and 3 around (so we can talk about 2 first)
PHE.NorthEast.network.components <- PHE.NorthEast.network.components %>%
dplyr::mutate(cluster.old=cluster, cluster=ifelse(cluster.old==2, 3, ifelse(cluster.old==3,2,cluster.old)))
PHE.NorthEast.network.components$Cluster <- paste0("Cluster",PHE.NorthEast.network.components$cluster)
# merge metadata back in
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.NorthEast.network.components$Taxa1, Cluster=PHE.NorthEast.network.components$Cluster), by="Taxa1", type="left")
Plot network
# Plot network
p.PHE.NorthEast.network.2SNP <- ggplot(PHE.NorthEast.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(Distance.Phylo), linetype=factor(Distance.Phylo))) +
scale_color_manual(values=c("grey5","grey55","grey85"), name="SNP\nDistance") +
scale_linetype(name="SNP\nDistance") +
theme_blank() +
ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
geom_nodes(size=1.0, aes(color=gender_orientation)) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
NULL
p.PHE.NorthEast.network.2SNP

Ok, so three networks. Clear differentiation of a heterosexual network
(with 0-snp distances) and two predominantly MSM networks
Let’s look at the phylogenetic context of those North East clusters
we’ve defined. Pull out subtrees (from sublineage 1 subtree)
# Cluster 1
Beast.tree.NE.cluster1 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster1","Taxa1"])
Beast.tree.NE.cluster1.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster1, levels_back=0)
p.Beast.tree.NE.cluster1.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster1.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 10)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Can't fit in tip labs, but since this is a polyphyletic subtree, it would be helpful to add a track to highlight the NE strains
PHE.metadata.linked$is.NorthEast <- ifelse(PHE.metadata.linked$phe_centre=="North East","North East", "Other England")
p.Beast.tree.NE.cluster1.subtree.cluster.highlight <- gheatmap(p.Beast.tree.NE.cluster1.subtree, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
ggnewscale::new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- gheatmap(p.Beast.tree.NE.cluster1.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*6),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6)) +
ggnewscale::new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID +
coord_cartesian(x=c(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))+2)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#######################
# Cluster 2
Beast.tree.NE.cluster2 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster2","Taxa1"])
Beast.tree.NE.cluster2.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster2, levels_back=1)
p.Beast.tree.NE.cluster2.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster2.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 20) + geom_tiplab(size=theme.text.size.within, align=T, offset=5, linesize=0.4)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster2.subtree <- gheatmap(p.Beast.tree.NE.cluster2.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster2.subtree$data$height)*3), offset=20+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster2.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster2.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster2.subtree <- p.Beast.tree.NE.cluster2.subtree +
coord_cartesian(x=c(p.Beast.tree.NE.cluster2.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster2.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))+0.5)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster2.subtree
############################
# Cluster 3
Beast.tree.NE.cluster3 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster3","Taxa1"])
Beast.tree.NE.cluster3.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster3, levels_back=1)
p.Beast.tree.NE.cluster3.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster3.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 26) + geom_tiplab(size=theme.text.size.within, align=T, offset=3, linesize=0.4)
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster3.subtree <- gheatmap(p.Beast.tree.NE.cluster3.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster3.subtree$data$height)*3), offset=26+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster3.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster3.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster3.subtree <- p.Beast.tree.NE.cluster3.subtree +
coord_cartesian(x=c(p.Beast.tree.NE.cluster3.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster3.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))+0.5)) +
theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster3.subtree
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#p.Beast.tree.NE.cluster2.subtree
#p.Beast.tree.NE.cluster3.subtree
Since Cluster 1 is really quite polyphyletic, it maybe more useful to
show the clusters in context for that one
# Add North East identifier column
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(sublineage.1.tree.heatmap, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(sublineage.1.tree.heatmap$data$height)*3)*1.2, offset=0+(4*5)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
ggnewscale::new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$height)*3)*1.2, offset=0+(4*6)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6, ncol=2)) +
ggnewscale::new_scale_fill()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
# add a bit more room to the x axis
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight +
coord_cartesian(x=c(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))+2))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# reduce spacing between legend scales
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + theme(legend.margin = margin(-0.95,0,0,0, unit="mm"))
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight

Plot together
p.Beast.tree.NE.subtrees.combi1 <- plot_grid(p.Beast.tree.NE.cluster2.subtree, p.Beast.tree.NE.cluster3.subtree, ncol=1, labels=c("C - Cluster 2", "D - Cluster 3"), vjust=1.0, label_size=panel.lab.size, scale=0.95)
p.Beast.tree.NE.subtrees.combi2 <- plot_grid(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(3,2), labels=c("B - Cluster 1", ""), label_size=panel.lab.size)
p.Beast.tree.NE.subtrees.combi2

p.Beast.tree.NE.subtrees.combi3 <- plot_grid(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(8,7), labels=c("B - Sublineage 1 (All)", ""), label_size=panel.lab.size, scale=0.95, vjust=1.0)
p.Beast.tree.NE.subtrees.combi3

Look more closely at population demographics of these clusters
# Metadata on NE cluster 2
PHE.metadata.linked %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster2.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Metadata on NE cluster 3
PHE.metadata.linked %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Country info on NE cluster 3
TPA.meta2.1 %>%
dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
dplyr::group_by(Geo_Country) %>%
dplyr::summarise(Count=n())
# Separate metadata records show Hungarian sample "TPA_HUN180001" came from a male bisexual (MSWM).
Examine SNP scaled tree for distances
# Extract information about SNP distances
TPA.NEcluster3.pyjartree.mrca <- getMRCA(TPA.pyjar.tree, as.character(unlist(TPA.meta2.1[TPA.meta2.1$Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label,"Sample_Name"])))
TPA.NEcluster3.pyjartree.subtree <- tree_subset(TPA.pyjar.tree, node=TPA.NEcluster3.pyjartree.mrca, levels_back=1)
ggtree(TPA.NEcluster3.pyjartree.subtree) + geom_tiplab(size=theme.text.size.within)

ggtree(TPA.NEcluster3.pyjartree.subtree)$data
Do some analysis of nearest neighbour and distances to MRCAs
calculate.years.from.mrca <- function(current.ggtree.phylo, current.ggtree.data){
#current.ggtree <- Beast.tree.NE.cluster3.subtree
all.tips <- current.ggtree.phylo$tip.label
dist.2.mrca <- NULL
### put dates into df
current.ggtree.data$mrca.median <- 2019.5 - current.ggtree.data$height_median
current.ggtree.data$year <- as.numeric(round(2019.5 - current.ggtree.data$height_median,3))
current.ggtree.data$mrca.95high <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[1]), 3)
current.ggtree.data$mrca.95low <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[2]), 3)
# extract dates between sample and its MRCA using loop
for (current.node in all.tips) {
current.parent <- c(match(current.node,current.ggtree.phylo$tip.label), phangorn::Ancestors(current.ggtree.phylo, match(c(current.node), current.ggtree.phylo$tip.label), "parent"))
current.nodelist <- current.ggtree.data[current.ggtree.data$node %in% current.parent,]
current.dist.2.mrca <- c(current.node, as.numeric(current.nodelist[1,"year"]-current.nodelist[2,"year"]))
dist.2.mrca <- rbind(dist.2.mrca, current.dist.2.mrca)
}
dist.2.mrca <- data.frame(Sample_Name=as.character(dist.2.mrca[,1]), dist.to.mrca=as.numeric(dist.2.mrca[,2]), stringsAsFactors=F)
return(dist.2.mrca)
}
### All samples in global tree
dist.mrca.all.TPA <- calculate.years.from.mrca(full.beast2.tree@phylo, full.beast2.tree@data)
Merge dist2MRCA with metadata
PHE.metadata.linked.dist2mrca <- left_join(PHE.metadata.linked, dist.mrca.all.TPA, by="Sample_Name")
p.time2mrca.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(gender_orientation, dist.to.mrca, color=gender_orientation)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="Gender Orientation", y="Years to MRCA", color="Gender Orientation") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=phe_centre)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip(ylim=c(0,40)) +
labs(x="UKHSA Region", y="Years to MRCA", color="UKHSA Region") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region)
p.time2mrca.phe_region.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=gender_orientation)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip(ylim=c(0,20)) +
labs(x="UKHSA Region", y="Years to MRCA") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region.orientation

p.time2mrca.sublineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA.pinecone.sublineage, dist.to.mrca, color=TPA.pinecone.sublineage)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="TPA Lineage", y="Years to MRCA", color="TPA Lineage") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
p.time2mrca.sublineage

p.time2mrca.Lineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA_Lineage, dist.to.mrca, color=TPA_Lineage)) +
geom_quasirandom(size=0.75, alpha=0.5) +
theme_light() + theme.text.size +
coord_flip() +
labs(x="TPA Lineage", y="Years to MRCA (Median of Posterior)", color="TPA Lineage") +
theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
scale_color_manual(values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage)
Maybe can make an MST of the North East samples for grapetree?
TPA.pyjar.tree.subset.NorthEast <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East","Sample_Name"])))
#ggtree(TPA.pyjar.tree.subset.NorthEast)
#write.tree(TPA.pyjar.tree.subset.NorthEast, paste0(Data_input_directory,"TPA.UK-only-NorthEast.pyjar.2022-02-26.tre"))
# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"
#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)
Alternative approach using MST instead of networks for North East
data
# Read in MST
#TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")
p.TPA.NorthEastEngland.Grapetree <- ggdraw() + draw_image(TPA.NorthEastEngland.Grapetree.file)
p.TPA.NorthEastEngland.Grapetree

p.TPA.NorthEastEngland.Grapetree.header <- plot_grid(p.TPA.NorthEastEngland.Grapetree, labels=c("A - Network Clusters (North East England)"), label_size=panel.lab.size, scale=0.95)
Plot with beast trees
#p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,6), labels=c("A - Network Clusters (North East England)", ""), label_size=panel.lab.size, scale = 0.95)
p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree.header, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,7))
p.PHE.NorthEast_MST.with.beast.subtrees.combi

#ggsave(paste0(Figure_output_directory,"Fig3_Sublin1.NorthEast.MST+Beast.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=200, height=245, device='pdf', dpi=1200)
#ggsave(plot=p.PHE.NorthEast_MST.with.beast.subtrees.combi, paste0(Figure_output_directory,"Fig3_Sublin1.NorthEast.MST+Beast.",format(Sys.Date(),"%Y%m%d"),".svg"), units='mm', width=200, height=245, device=svglite, dpi=1200)
Do some analysis of major sublineages over time by region - could this
influence observations about sublineages?
# Generate some stats by PHE Region
PHE.major.sublineage.PHEcentre.date <- PHE.metadata.linked %>%
dplyr::filter(TPA.pinecone.sublineage %in% c(1,14)) %>%
dplyr::group_by(TPA.pinecone.sublineage, phe_centre, year) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.sublin=sum(Count)) %>%
dplyr::arrange(desc(phe_centre), .by_group=T) %>%
dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'phe_centre'. You can override using the `.groups` argument.
ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, phe_centre, size=Count, color=TPA.pinecone.sublineage)) +
geom_point() +
facet_grid(.~TPA.pinecone.sublineage) +
theme_light() +
theme.text.size +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)

p.PHE.major.sublineage.PHEcentre.date.bubbleplot <- ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, TPA.pinecone.sublineage, color=TPA.pinecone.sublineage)) +
geom_point(alpha=0.65, aes(size=Count)) +
geom_line(alpha=0.25) +
facet_grid(factor(gsub("\\ ","\n",phe_centre), levels=gsub("\\ ","\n",PHE.region.cols.brew$UKHSA.region))~., switch='y') +
theme_light() +
theme(strip.placement = "outside") +
theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y=element_text(color = "grey25",angle=0, size=5)) +
scale_size_area(max_size = 4.5,breaks=c(1,5,10,20,30,40)) +
theme.text.size +
scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
labs(y="Region", x="Year", color="Sublineage")
p.PHE.major.sublineage.PHEcentre.date.bubbleplot

Do some specific analysis for the 3 Northern regions
# Generate some stats by PHE Region
PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::summarise(count=n())
PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(year) %>%
dplyr::summarise(count=n())
p.PHE.major.sublineage.3NorthernRegions <- PHE.metadata.linked %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(TPA.pinecone.sublineage, year, phe_centre) %>%
dplyr::summarise(Count=n()) %>%
ggplot(aes(year, Count, fill=phe_centre)) +
geom_bar(stat='identity', width=0.65) +
scale_fill_manual(values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
theme_bw() + theme.text.size +
scale_x_continuous(breaks=seq(2012,2018,1)) +
scale_y_continuous(breaks=pretty) +
labs(title="Samples in 3 Northern Regions", x="Collection Year", y="Sample Count", fill="Public Health\nRegion") +
theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
#geom_text(aes(x=year,y=Count-0.5, label=Count), color='grey95', size=theme.text.size.within) +
NULL
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'year'. You can override using the `.groups` argument.
p.PHE.major.sublineage.3NorthernRegions

Single linkage network of identical genomes from UK
# Constrain by SNP distance (identical in the asr snp tree)
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta[PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,]
# and a max of 2 years
#PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$decimal.date.distance<=2,]
# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$Distance.Phylo),]
# remove self-samples
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$same.sample=="different",]
# cleanup some data noise
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$year.t1),]
# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat.years","epi.time.distance.cat")]
############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.identicals.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.identicals.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.identicals.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals.input1[!duplicated(PHE.alignment.data.dist.melt.meta.identicals.input1$edgename),]
# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2)
############
# Deduplicate
#inverse weight
PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance.inv <- 1/1/(PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance+0.04)
# Make actual network
set.seed(1236)
PHE.identicals.network <- network(PHE.alignment.data.dist.melt.meta.identicals.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F, loops = F)
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "kamadakawai", weights = "decimal.date.distance.inv")
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold", weights = "decimal.date.distance")
PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold")
PHE.identicals.network.gg$Taxa1 <- PHE.identicals.network.gg$vertex.names
# extract temporal clusters from network
PHE.identicals.network.ig <- asIgraph(PHE.identicals.network)
PHE.identicals.network.components <- data.frame(Taxa1=network.vertex.names(PHE.identicals.network), vertex.no=as.vector(V(PHE.identicals.network.ig)), cluster=igraph::components(PHE.identicals.network.ig)$membership)
PHE.identicals.network.components$Cluster <- paste0("Cluster",PHE.identicals.network.components$cluster)
# merge metadata back in
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster), by="Taxa1", type="left")
#
# Add temporal colour scale
#unique(PHE.identicals.network.gg$epi.time.distance.cat)
epi.time.distance.cat.cols <- rev(colorRampPalette(brewer.pal(8, "Greys"))(length(unique(PHE.identicals.network.gg$epi.time.distance.cat))-1))
# Plot network
p.PHE.identicals.network.0SNP <- ggplot(PHE.identicals.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(epi.time.distance.cat), linetype=factor(epi.time.distance.cat))) +
#scale_color_manual(values=c("grey5","grey35","grey55", "grey65", "grey75"), name="SNP\nDistance") +
scale_color_manual(name="Temporal\nDistance", values = epi.time.distance.cat.cols) +
scale_linetype(name="Temporal\nDistance") +
theme_blank() +
ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
#geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
geom_nodes(size=2.5, aes(color=gender_orientation), alpha=0.9) +
scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
NULL
p.PHE.identicals.network.0SNP

Plot this against a UK tree?
gheatmap(ggtree(TPA.pyjar.tree.subset.uk),
data.frame(row.names=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster))

Some stats from this
p.PHE.identical.Orientation_class.bydatedist <- PHE.alignment.data.dist.melt.meta %>%
dplyr::filter(same.sample=="different", Distance.Phylo==0) %>%
#filter(decimal.date.distance<=1) %>%
dplyr::group_by(epi.time.distance.cat, Orientation.Class) %>%
dplyr::summarise(Count.class.date=n()) %>%
dplyr::mutate(sum.class=sum(Count.class.date), fract.class=Count.class.date/sum.class) %>%
ggplot(aes(x=epi.time.distance.cat, y=Count.class.date, fill=Orientation.Class)) +
geom_bar(stat='identity', position='stack') +
theme_bw() +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Time between samples", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'epi.time.distance.cat'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.bydatedist

p.PHE.identical.Orientation_class.byZerodist.cluster <- PHE.identicals.network.gg %>%
dplyr::filter(!is.na(Orientation.Class)) %>%
dplyr::group_by(Cluster, Orientation.Class) %>%
dplyr::summarise(Count.class.cluster=n()) %>%
dplyr::mutate(sum.class=sum(Count.class.cluster), fract.class=Count.class.cluster/sum.class) %>%
dplyr::arrange(desc(sum.class)) %>%
dplyr::ungroup() %>%
dplyr::mutate(Cluster=as_factor(Cluster)) %>%
ggplot(aes(x=Cluster, y=Count.class.cluster, fill=Orientation.Class)) +
geom_bar(stat='identity', position='stack') +
theme_bw() +
x.theme.axis.rotate +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Identical Genome Cluster", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.byZerodist.cluster

d.PHE.identical.GenderOrientation.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
dplyr::group_by(TPA.pinecone.sublineage, Cluster, gender_orientation) %>%
dplyr::summarise(count.orient.cluster=n()) %>%
dplyr::mutate(count.cluster=sum(count.orient.cluster), fract=count.orient.cluster/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(count.cluster)) %>%
dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
d.PHE.identical.GenderOrientation.byZerodist.cluster
# Plot sample counts by genome cluster (coloured by orientation)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
ggplot(aes(Cluster.o, count.orient.cluster, fill=gender_orientation)) +
geom_bar(stat="identity", width=0.65) +
scale_fill_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation, guide = guide_legend(order = 1)) +
theme_light() +
x.theme.axis.rotate +
scale_y_continuous(breaks=seq(0,45,5)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
labs(x="Identical Genome Cluster", y="Sample Count", fill="Patient Gender Orientation")
# Add details of sublineage
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster +
ggnewscale::new_scale_color() +
geom_point(data=(d.PHE.identical.GenderOrientation.byZerodist.cluster %>% select(Cluster.o, TPA.pinecone.sublineage) %>% distinct()), aes(Cluster.o, -1.5, color=TPA.pinecone.sublineage), inherit.aes = F) + scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, name="Sublineage", guide = guide_legend(order = 2)) +
NULL
# Add a sublineage axis label (bit of a hack)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster +
geom_text(data=data.frame(lab="Sublineage", y=-1.5, x=28, stringsAsFactors=F), aes(label=lab, x=x, y=y), hjust = 0.1, size=theme.text.size.within, inherit.aes = F) +
coord_cartesian(x=c(1, 27), clip='off')
p.PHE.identical.GenderOrientation.byZerodist.cluster

######gxxxxgsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=100, device='pdf', dpi=1200)
Possible to introduce some more info into that plot?
d.PHE.identical.region.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
dplyr::group_by(TPA.pinecone.sublineage, Cluster, phe_centre) %>%
dplyr::summarise(count.region.cluster=n()) %>%
dplyr::mutate(count.cluster=sum(count.region.cluster), fract=count.region.cluster/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(count.cluster)) %>%
dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Region.byZerodist.cluster <- d.PHE.identical.region.byZerodist.cluster %>%
ggplot(aes(Cluster.o, count.region.cluster, fill=phe_centre)) +
geom_bar(stat="identity", width=0.65, position='fill') +
scale_fill_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, guide = guide_legend(order = 1)) +
theme_light() +
x.theme.axis.rotate +
scale_y_continuous(breaks=seq(0,45,5)) +
theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
guides(fill=guide_legend(ncol=2)) +
labs(x="Identical Genome Cluster", y="Region Proportion", fill="UKHSA Region")
p.PHE.identical.byZerodist.cluster.barcombi <- plot_grid(p.PHE.identical.GenderOrientation.byZerodist.cluster + x.theme.strip, p.PHE.identical.Region.byZerodist.cluster, ncol=1, axis="rlt", align=T, rel_heights = c(2,1), labels=c("B","C"), label_size=panel.lab.size)
#p.PHE.identical.byZerodist.cluster.barcombi
#p.PHE.identicals.network.0SNP
plot_grid(p.PHE.identicals.network.0SNP, p.PHE.identical.byZerodist.cluster.barcombi, ncol=1, rel_heights=c(2,3), labels=c("A",""), label_size=panel.lab.size)

p.PHE.identical.byZerodist.cluster.barcombi.noNet <- plot_grid(p.PHE.identical.GenderOrientation.byZerodist.cluster + x.theme.strip, p.PHE.identical.Region.byZerodist.cluster, ncol=1, axis="rlt", align=T, rel_heights = c(2,1), labels=c("A","B"), label_size=panel.lab.size)
p.PHE.identical.byZerodist.cluster.barcombi.noNet

#ggsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=120, device='pdf', dpi=1200)
Get a few more stats on the largest cluster (Cluster 8)
#d.PHE.identical.GenderOrientation.byZerodist.cluster %>% filter(Cluster=="Cluster8")
PHE.identicals.network.gg.identical.cluster8 <- PHE.identicals.network.gg %>% filter(Cluster=="Cluster8") %>%
select(vertex.names, Orientation.Class, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos, Cluster)
sort(unique(PHE.identicals.network.gg.identical.cluster8$year))
[1] 2012 2013 2014 2015 2016 2017 2018
Get some more information about the heterosexual only clusters
PHE.identicals.network.gg.identical_heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %in% c("Cluster12", "Cluster20", "Cluster27")) %>%
select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>%
distinct() %>%
arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_heteroclusters
And do the same for the small mixed/GBMSM clusters
PHE.identicals.network.gg.identical_not.heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %notin% c("Cluster12", "Cluster20", "Cluster27", "Cluster8")) %>%
select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>%
distinct() %>%
arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_not.heteroclusters
What proportion of heterosexuals have an identical GBMSM paired
genome?
# Delineate heterosexual clusters
d.PHE.identical.heterosexual.clusters <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
dplyr::mutate(is.heterosexual=ifelse(gender_orientation%in% c("MSW", "WSM"), "heterosexual", ifelse(gender_orientation=="GBMSM","GBMSM", "Unknown"))) %>%
dplyr::group_by(Cluster,is.heterosexual) %>%
dplyr::mutate(count.hetero=sum(count.orient.cluster), fract.hetero=sum(count.orient.cluster)/count.cluster) %>%
dplyr::ungroup() %>%
dplyr::filter(is.heterosexual=="heterosexual") %>%
dplyr::select(-c(count.orient.cluster, gender_orientation, fract)) %>%
dplyr::distinct() %>%
dplyr::mutate(cluster.type=ifelse(fract.hetero==1, "hetero.only", "other"))
d.PHE.identical.heterosexual.clusters
# What proportion of heterosexuals (n=20) are in a heterosexual-only cluster?
d.PHE.identical.heterosexual.clusters %>%
dplyr::group_by(cluster.type) %>%
dplyr::summarise(count.in.hetero.cluster=sum(count.hetero)) %>%
dplyr::mutate(fract.in.hetero=count.in.hetero.cluster/sum(count.in.hetero.cluster))
#left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name"))
Revisions 03-2023 onwards
Look at proportion of genomes at different coverage thresholds
# Cumulative proportion of N counts in genomes
PHE.metadata.Ncount.cummulative.UK <- PHE.metadata.linked %>%
dplyr::filter(is.UK=="UK") %>%
dplyr::group_by(`Proportion-N_>5_mapping+masking_Nichols`) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.Count=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.Count, cum_fract=cumsum(fraction), cum_count=cumsum(Count)) %>%
dplyr::mutate(Dataset="UK (n=237)")
PHE.metadata.Ncount.cummulative.UK
PHE.metadata.Ncount.cummulative.ALL <- TPA.meta2.1 %>%
dplyr::filter(full.temporal.analysis=="Yes") %>%
dplyr::group_by(`Proportion-N_>5_mapping+masking_Nichols`) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.Count=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.Count, cum_fract=cumsum(fraction), cum_count=cumsum(Count)) %>%
dplyr::mutate(Dataset="All (n=520)")
PHE.metadata.Ncount.cummulative.ALL
PHE.metadata.Ncount.cummulative.combi <- rbind(PHE.metadata.Ncount.cummulative.UK, PHE.metadata.Ncount.cummulative.ALL)
p.cumulative.Ncount.for.datset <- ggplot(PHE.metadata.Ncount.cummulative.combi , aes(`Proportion-N_>5_mapping+masking_Nichols`, cum_fract, group=Dataset, color=Dataset)) +
geom_point(alpha=0.75, size=1) +
theme_light() +
theme.text.size + theme(legend.position = 'top') +
labs(y="Cumulative fraction of genomes", x="Proportion of sites masked to N") +
scale_y_continuous(breaks=seq(0,1,0.1))
p.cumulative.Ncount.for.datset

BEAST 95% HPD calculations (provide more details for 520 dataset )
BEAST.median <- 1.28e-7
BEAST.95HPD <- c(1.07e-7, 1.48e-7)
SS14.aln.length <- 1139569
1/(BEAST.median * SS14.aln.length)
[1] 6.855662
1/(BEAST.95HPD * SS14.aln.length)
[1] 8.201166 5.929221
Further evaluation of sublineage 6 (reviewer response) using ancestral
reconstruction performed on the global TPA-only alignment/tree used in
Beale 2021.
TPA.treetime.ancestral.tree <- read.nexus(TPA.treetime.ancestral.tree.file)
TPA.treetime.ancestral.tree.data <- fortify(TPA.treetime.ancestral.tree)
ggtree(TPA.treetime.ancestral.tree) + geom_nodelab(size=2)

# Read in and process TPA-only vcf (to confirm sites are the same)
TPA.only.midpoint.treetime.ancestral.vcf <- read.vcfR(TPA.treetime.ancestral.vcf.file, verbose = FALSE)
Error in read.vcfR(TPA.treetime.ancestral.vcf.file, verbose = FALSE) :
could not find function "read.vcfR"
Extract genotype sites
Use snpEff to annotate multi-vcf, and then pull in annotations
here
Lets pull in gene function (where known) for these sites from the
gff
# read in snp classifications, and apply to discriminatory SNPs
Write this as a function. Takes 4 arguments: - dataframe of snps for
each sample in wide matrix format
(e.g. TPA.treetime.ancestral.vcf.gt.f.spread) - longform list of SNPs
and possible alleles (e.g. TPA.treetime.ancestral.vcf.fix) - variant
annotations dataframe (e.g. TPA.snpEff.filt) - a vector of two nodes in
the tree to compare (e.g. tt.nodes.to.compare.SS14)
Do some further analysis of the North East sublineage distributions. We
have 35 samples collected from these regions, of which 17 were collected
from 2014 onwards. Is sublineage 14 missing by chance (could we be
missing it simply because we haven’t collected enough samples) or is
this more likely to reflect true uneven regional distributions?
# How many genomes found in Northern regions before and after first detection of sublineage 14 in 2014?
PHE.metadata.linked %>%
dplyr::mutate(before2014=ifelse(year>=2014,"2014onwards", "pre2014")) %>%
dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
dplyr::group_by(before2014) %>%
dplyr::summarise(count=n())
# What are the proportions of different sublineages around the UK before and after 2014?
PHE.meta.post2014.sublin.fracs <- PHE.metadata.linked %>%
#dplyr::filter(year>=2014) %>%
dplyr::mutate(before2014=ifelse(year>=2014,"2014onwards", "pre2014")) %>%
dplyr::group_by(before2014, TPA.pinecone.sublineage) %>%
dplyr::summarise(Count=n()) %>%
dplyr::mutate(total.all=sum(Count)) %>%
dplyr::mutate(fraction=Count/total.all) %>%
dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
dplyr::mutate(cum_fract = cumsum(fraction)) %>%
dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))
`summarise()` has grouped output by 'before2014'. You can override using the `.groups` argument.
PHE.meta.post2014.sublin.fracs
# simulating poisson process r to work out how many samples we would expect in Northern England under poisson distribution
# What % of sublineage 14 samples are found in the total population?
post2014.sublin14.freq <- PHE.meta.post2014.sublin.fracs %>% filter(before2014=="2014onwards", TPA.pinecone.sublineage==14) %>% select(Lineage.perc) %>% pull()
Adding missing grouping variables: `before2014`
# Simulate and plot a Poisson distribution of how many sublineage 14 samples we would expect to find if we randomly selected 17 samples at 22%
data.frame(rpois=rpois(1000000, 17/(100/post2014.sublin14.freq))) %>%
ggplot(aes(rpois)) + geom_histogram(binwidth=1) +
scale_x_continuous(breaks=seq(0,20,2)) +
theme_light() +
labs(x="Samples Found", y="Simulation Count")

# What are the quantile distributions from that?
quantile(rpois(1000000, 17/(100/post2014.sublin14.freq)), probs=c(0.01, 0.05, 0.5, 0.95, 0.99))
1% 5% 50% 95% 99%
0 1 4 7 9
median(rpois(1000000, 17/(100/post2014.sublin14.freq)))
[1] 4
mean(rpois(1000000, 17/(100/post2014.sublin14.freq)))
[1] 3.796758
# What is the probability of finding no samples (assuming uniform unbiased coverage)?
data.frame(n=seq(0,20,1), dpois=sapply(seq(0,20,1), function(x) dpois(x, lambda=17/(100/post2014.sublin14.freq)))) %>%
ggplot(aes(x=n, y=dpois)) +
geom_bar(stat='identity') +
scale_x_continuous(breaks=pretty) +
theme_light() +
labs(x="Samples Found", y="Probability")

paste("Probability of finding zero samples is ", round(dpois(0, lambda=17/(100/post2014.sublin14.freq)), 5))
[1] "Probability of finding zero samples is 0.02244"
September 2023 - Pull out some additional statistics/percentages
requested by subeditor for final manuscript proofs
# Counts and % of each gender
PHE.metadata.linked %>%
dplyr::mutate(Gender=ifelse(gender_orientation %in% c("GBMSM", "MUnknown", "MSW"), "Male", ifelse(gender_orientation %in% c("WSM", "WSW"), "Female", "Unknown"))) %>%
dplyr::mutate(total.samples=n()) %>%
dplyr::group_by(Gender) %>%
dplyr::summarise(Gender.Count=n(), Gender.Perc=(Gender.Count/237)*100)
# Exact dates of sampling frame
decimal2Date(max(PHE.metadata.linked$date.decimal)) # last sample (revise to end of month)
[1] "2018-10-01"
decimal2Date(min(PHE.metadata.linked$date.decimal)) # first sample (revise to start of month)
[1] "2012-01-01"
# Where did those last samples come from - are they non-PHE, and when was the last UKHSA sample?
PHE.metadata.linked %>%
select(Sample_Name, date.decimal) %>%
arrange(date.decimal)
# Counts and % of heterosexuals & GBMSM in UKHSA dataset (as opposed to combined UKHSA + prospective)
PHE.metadata.linked %>%
dplyr::filter(is.PHE=="PHE") %>%
dplyr::group_by(gender_orientation) %>%
dplyr::summarise(count=n())
NA
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFRyZXBvbmVtYSBVSyBVS0hTQS1jb2hvcnQgQW5hbHlzaXMgMjAyMi4gUmV2aXNpb24gMDQtMjAyMyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKTWFrZSBhIGNsZWFuIGVudmlyb25tZW50CmBgYHtyfQogcm0obGlzdD1scygpKQpgYGAKXApMb2FkIHBhY2thZ2VzCmBgYHtyfQpwYWNrYWdlcy5saXN0IDwtIGMoImdncGxvdDIiLCJ0cmVlaW8iLCJnZ3RyZWUiLCJnZ25ld3NjYWxlIiwiYXBlIiwiZHBseXIiLCJ0aWR5dmVyc2UiLCJ0aWR5ciIsInBoeXRvb2xzIiwiUkNvbG9yQnJld2VyIiwibHVicmlkYXRlIiwicmVhZHhsIiwiZ2dmb3JjZSIsImdnc3RhbmNlIiwiZ2dyaWRnZXMiLCJjb3dwbG90IiwiaGV4YmluIiwic2NhbGVzIiwiaGF2ZW4iLCJuZXR3b3JrIiwiZ2duZXR3b3JrIiwiaW50ZXJncmFwaCIsImlncmFwaCIsImdncmFwaCIsImdyYXBobGF5b3V0cyIsInNjYXR0ZXJwaWUiLCJtYXBzIiwibWFwZGF0YSIsIm1hcHRvb2xzIiwicmdkYWwiLCJyZ2VvcyIsImJyb29tIiwiZ2dyZXBlbCIsImdncmlkZ2VzIiwibWFnaWNrIiwiZ2diZWVzd2FybSIsImdncmFzdHIiLCAiZXh0cmFmb250Iiwic3ZnbGl0ZSIpCgojInBseXIiLCJDYWlybyIsImdnbWFwIiwiZW1vamlmb250IiwiclBpbmVjb25lIiwicGFpcnNucCIsIkNvb3JkaW5hdGVDbGVhbmVyIiwiZ3JpZEV4dHJhIiwiZGVuZGV4dGVuZCIsImdnZGVuZHJvIiwKCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZ2d0cmVlIikKI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJ0cmVlaW8iKQoKZm9yKHBrZyBpbiBwYWNrYWdlcy5saXN0KXsKICBldmFsKGJxdW90ZShsaWJyYXJ5KC4ocGtnKSkpKSB9CmBgYApcCkNvbmZpcm0gY3VycmVudCBlbnZpcm9ubWVudGFsIHNldHVwCmBgYHtyfQpSLlZlcnNpb24oKQpwcmludChzZXNzaW9uSW5mbygpKQpgYGAKXApNYWtlIHNvbWUgc2hvcnRjdXRzIGZvciBwbG90dGluZyAKYGBge3J9CnkudGhlbWUuc3RyaXAgPC0gdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCnkudGhlbWUuc3RyaXAucGFydGlhbCA8LSB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCgp4LnRoZW1lLnN0cmlwIDwtIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLnBhcnRpYWwgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLmxhYnMgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKeC50aGVtZS5heGlzLnJvdGF0ZSA8LSB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpsZWdlbmQuc3RyaXAgPC0gdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKdGhlbWUudGV4dC5zaXplIDwtIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCiclbm90aW4lJyA8LSBOZWdhdGUoJyVpbiUnKQoKbWF4LmZvbnQuc2l6ZSA8LSA3CmJhc2ljLmZvbnQuc2l6ZSA8LSA2Cm1pbi5mb250LnNpemUgPC0gNS4yNQp0aGVtZS50ZXh0LnNpemUgPC0gdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gYmFzaWMuZm9udC5zaXplKSkKdGhlbWUudGV4dC5zaXplLndpdGhpbiA8LSAoNS8xNCkqbWluLmZvbnQuc2l6ZQpwYW5lbC5sYWIuc2l6ZSA8LSAxMAoKYGBgClwKU3BlY2lmeSByYXcgZGF0YSAtIGdsb2JhbCBkYXRhc2V0CmBgYHtyfQojRGF0YV9pbnB1dF9kaXJlY3RvcnkgPC0gIi9Vc2Vycy9tYjI5L1BhcGVycy9UcmVwb25lbWFfVUstUEhFLWdlbi1lcGlfMjAyMS9EYXRhLyIKI0RhdGFfaW5wdXRfZGlyZWN0b3J5IDwtICIvVXNlcnMvbWIyOS9QYXBlcnMvVHJlcG9uZW1hX1VLLVBIRS1nZW4tZXBpXzIwMjEvUm5vdGVib29rL1Jub3RlYm9va18wOS0yMDIyL2RhdGEvIgpEYXRhX2lucHV0X2RpcmVjdG9yeSA8LSBwYXN0ZTAoZ2V0d2QoKSwgIi9pbnB1dGRhdGEvIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIFRyZWUgZGF0YSAKCiMgTUwgdHJlZSAocmVmaW5lZCBkYXRhc2V0KQpUUEEuTUx0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQuZml4LXplcm8tZGlzdC50cmVlZmlsZSIpCgojIFB5amFyIHRyZWUgKHJlZmluZWQgZGF0YXNldCkKVFBBLnB5amFyLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQucHlqYXIudHJlIikKCiMgRnVsbCBzaXplIEJFQVNUMiBhbmFseXNpcyAtIHByZXZpb3VzbHkgZ2VuZXJhdGVkIGFzIHBhcnQgb2YgQmVhbGUsIDIwMjEuCmZ1bGwuYmVhc3QyLnRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyX2JlYXN0Ml9zdHJpY3Qtc2t5bGluZS01MDBNXzEwcG9wX2NvbnNlbnN1cy50cmVlIikKCiMgQW5jZXN0cmFsIHJlY29uc3RydWN0aW9uIG9mIGdsb2JhbCBUUEEgTUwgdHJlZSBmcm9tIFRyZWVUaW1lIChyZWZpbmVkIGRhdGFzZXQpClRQQS50cmVldGltZS5hbmNlc3RyYWwudHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLmFubm90YXRlZF90cmVlLmZpeC1odW5nLm5leHVzIikKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyLm1pZHBvaW50LmFuY2VzdHJhbF9zZXF1ZW5jZXMuZml4LWh1bmcudmNmIikKIyBGdW5jdGlvbmFsbHkgYW5ub3RhdGVkIHZhcmlhbnRzLCBleHRyYWN0ZWQgZnJvbSBzbnBFZmYgdmNmIGludG8gdHN2IHVzaW5nIHNucFNpZnQKVFBBLnNucEVmZi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLXViZXIubWlkcG9pbnQuYW5jZXN0cmFsX3NlcXVlbmNlcy5yZWxhYi5iY2YuYW5uLnZjZi52YXJ0YWIuc2VwbGluZS50c3YiKQojIEdmZiBmaWxlIGZvciBTUzE0IHJlZmVyZW5jZSBnZW5vbWUsIGNvbnRhaW5pbmcgZ2VuZSBwb3NpdGlvbnMvYW5ub3RhdGlvbnMKU1MxNC5nZmYuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRyZXBvbmVtYV9wYWxsaWR1bV9zdWJzLl9wYWxsaWR1bV9TUzE0Lk5DXzAyMTUwOC4xLjIwMjEtMDYtMTMuZ2ZmIikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgTWV0YSBkYXRhIAoKIyBTdXBwbGVtZW50IGZyb20gVFBBLVViZXIgcGFwZXIgLSBCZWFsZSwgMjAyMSAKVFBBLm1ldGEyLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJTdXBfRGF0YTFfR2xvYmFsX1NhbXBsZS1NZXRhZGF0YV9fMDktMjAyMi54bHN4IikKCiMgRW5nbGFuZCBzcGVjaWZpYyBtZXRhZGF0YSBjb2xsYXRlZCBieSBQSEUvVUtIU0EKUEhFLm1ldGFkYXRhLmxpbmtlZC5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiU3VwX0RhdGEyX1RQQS5VSy1vbmx5LlBIRS5tZXRhZGF0YS4yMDIyLTAyLTAyLnhsc3giKQoKIyBFbmdsYW5kIHNwZWNpZmljIG1hcHBpbmcgc2hhcGVmaWxlIGRhdGEgd2l0aCBQdWJsaWMgSGVhbHRoIEJvdW5kYXJpZXMKIyBJbXBvcnRlZCBkYXRhZmlsZSBmcm9tIGh0dHBzOi8vZ2VvcG9ydGFsLnN0YXRpc3RpY3MuZ292LnVrL2RhdGFzZXRzL3B1YmxpYy1oZWFsdGgtZW5nbGFuZC1jZW50cmVzLWRlY2VtYmVyLTIwMTYtZnVsbC1jbGlwcGVkLWJvdW5kYXJpZXMtaW4tZW5nbGFuZC9leHBsb3JlP2xvY2F0aW9uPTUyLjk1MDAwMCUyQy0yLjAwMDAwMCUyQzYuODgKVUsucHVibGljaGVhbHRoLnNoYXBlZmlsZS5kYXRhIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiUHVibGljX0hlYWx0aF9FbmdsYW5kX0NlbnRyZXNfKERlY2VtYmVyXzIwMTYpX0JvdW5kYXJpZXMiKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgRXh0ZXJuYWxseSBwbG90dGVkIGZpZ3VyZXMgKGUuZy4gR3JhcGVUcmVlKSBmb3IgaW5jbHVzaW9uIGluIG11bHRpcGFuZWwgZmlndXJlcwoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIGNvZGUgdG8gZXh0cmFjdCBzdWJ0cmVlIHRoYXQgd2FzIHVzZWQgdG8gbWFrZSB0aGlzIGlzIGluY2x1ZGVkIGxhdGVyIGluIHRoaXMgUm5vdGVib29rClRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy0yMDIyLTAyLTAzLnN1YmxpbmVhZ2UtTVNUcmVlLklua3NjYXBlZC5zdmciKQoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIDMtdmFyaWFibGUgcGxvdHMKVFBBLlVLLkdyYXBldHJlZS4zd2F5LmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstMjAyMi0wMi0xNi4tTVNUcmVlXzMtd2F5LWZpZ3VyZS5JbnNjYXBlZC0zLnN2ZyIpCgojIEV4dGVybmFsbHkgcGxvdHRlZCBncmFwZXRyZWUgbWluaW11bSBzcGFubmluZyB0cmVlIGZvciB3aG9sZSBvZiBFbmdsYW5kIC0gSElWIHN0YXR1cwpUUEEuVUsuR3JhcGV0cmVlLkhJVi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLTIwMjItMDItMDMuSElWc3RhdHVzLU1TVHJlZV9pbmtzY2FwZWQuc3ZnIikKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIE5vcnRoIEVhc3QgRW5nbGFuZCBuZXR3b3JrcwpUUEEuTm9ydGhFYXN0RW5nbGFuZC5HcmFwZXRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy1Ob3J0aEVhc3QtMjAyMi0wMi0yNi5HZW5kZXJPcmllbnRhdGlvbi1NU1RyZWUuaW5rc2NhcGVkLitub2RlLWNvdW50cytHQk1TTS5zdmciKQoKCmBgYApcClNwZWNpZnkgZGlyZWN0b3J5IHRvIG91dHB1dCBwbG90cwpgYGB7cn0KRmlndXJlX291dHB1dF9kaXJlY3RvcnkgPC0gcGFzdGUwKGdldHdkKCksICIvRmlndXJlc19yZXZpc2lvbl8wMy0yMDIzLyIpCgojIi9Vc2Vycy9tYjI5L1BhcGVycy9UcmVwb25lbWFfVUstUEhFLWdlbi1lcGlfMjAyMS9GaWd1cmVzL0ZpZ3VyZV9EcmFmdGluZy9Xb3JraW5nX0ZpZ3VyZXNfMDgtMjAyMi8iCmBgYApcClJlYWQgaW4gdHJlZXMKYGBge3J9ClRQQS5NTHRyZWUgPC0gbWlkcG9pbnQucm9vdChyZWFkLnRyZWUoVFBBLk1MdHJlZS5maWxlKSkKVFBBLnB5amFyLnRyZWUgPC0gbWlkcG9pbnQucm9vdChyZWFkLnRyZWUoVFBBLnB5amFyLmZpbGUpKQpgYGAKXApSZWFkIGluIGZpbmFsIG91dHB1dCBtZXRhZGF0YSBmcm9tIEdsb2JhbCBVYmVyIHN0dWR5IChCZWFsZSAyMDIxKQpgYGB7cn0KVFBBLm1ldGEyLjEgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKFRQQS5tZXRhMi5maWxlLHNoZWV0PSJTdXBwbGVtZW50YXJ5X0RhdGExX1NhbXBsZS1NZXRhIikKYGBgClwKQ3JlYXRlIGEgY29sb3VyIHNjaGVtZSBmb3IgTGluZWFnZXMsIENvdW50cmllcyBhbmQgQ29udGluZW50cyAoY29uc2lzdGVudCB3aXRoIEJlYWxlLCAyMDIxKQpgYGB7cn0KIyBDb2xvdXJpbmcgZm9yIGNvdW50cnkKY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyIDwtIHVuaXF1ZShUUEEubWV0YTIuMVssYygiR2VvX0NvdW50cnkiLCJDb250aW5lbnQiKV0pCmNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiA8LSBjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzJbb3JkZXIoY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJENvbnRpbmVudCxjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkR2VvX0NvdW50cnkpLF0KCmNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiRjb3VudHJ5LmNvbCA8LSBjKCIjZWM3MDE0IiwiI2ZlYzQ0ZiIsIiNkZTJkMjYiLCIjZmI2YTRhIiwiI2JkYmRiZCIsIiM3MzczNzMiLGJyZXdlci5wYWwobj04LCJQdXJwbGVzIilbNDo4XSxicmV3ZXIucGFsKG49OCwiQmx1ZXMiKVszOjhdLGJyZXdlci5wYWwobj01LCJHcmVlbnMiKVszOjVdLCIjYzUxYjhhIiwiIzhjNTEwYSIpCgojIENvbG91cmluZyBmb3IgQ29udGluZW50CmNvbnRpbmVudGFsLmNvbHMuYnJldzIgPC0gZGF0YS5mcmFtZShDb250aW5lbnQ9c29ydCh1bmlxdWUoVFBBLm1ldGEyLjEkQ29udGluZW50KSksc3RyaW5nc0FzRmFjdG9ycz1GKQpjb250aW5lbnRhbC5jb2xzLmJyZXcyJGNvbnRpbmVudC5jb2wgPC0gYygiI2ZlYzQ0ZiIsIiNkZTJkMjYiLCIjYmRiZGJkIiwiIzIxNzFiNSIsIiM3NGM0NzYiLCIjYzUxYjhhIiwiI2VjNzAxNCIpCgoKIyBDb2xvdXJpbmcgZm9yIFRQQSBMaW5lYWdlClRQQV9MaW5lYWdlLmNvbHMgPC0gZGF0YS5mcmFtZShMaW5lYWdlPXNvcnQodW5pcXVlKFRQQS5tZXRhMi4xJFRQQV9MaW5lYWdlKSksc3RyaW5nc0FzRmFjdG9ycz1GKQpUUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sIDwtIGMoInJveWFsYmx1ZTIiLCAiaW5kaWFucmVkMSIpCiNjKCIjNDM2ZWVlIiwgIiM2NjY2NjYiLCIjZmY2YTZhIikKVFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlIDwtIGZhY3RvcihUUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UsIGxldmVscz1jKCJOaWNob2xzIiwiU1MxNCIsIm91dGxpZXIiKSkKCiMgTGluZWFnZSBIZXhjb2RlcwojIHJveWFsYmx1ZTIgIzQzNmVlZQojIGluZGlhbnJlZDEgI2ZmNmE2YQpgYGAKXApEZWZpbmUgY29sb3VycyBmb3Igc3VibGluZWFnZXMKYGBge3J9CiMgRGVmaW5lIHN1YmxpbmVhZ2UgY2x1c3RlcmluZyBzY2hlbWUgdXNpbmcgYnJldyBjb2xvdXJzY2FsZXMKc3VibGluZWFnZXMuY29scy5icmV3IDwtIGRhdGEuZnJhbWUodW5pcXVlKFRQQS5tZXRhMi4xWyxjKCJUUEFfTGluZWFnZSIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIildKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbb3JkZXIoc3VibGluZWFnZXMuY29scy5icmV3JFRQQV9MaW5lYWdlLHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSksXQoKc3VibGluZWFnZXMuY29scy5icmV3JHN1Ymxpbi5vcmRlciA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gc3VibGluZWFnZXMuY29scy5icmV3W29yZGVyKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW4ub3JkZXIpLF0KCiMgRm9yIHJldmlzZWQgYm9vdHN0cmFwcGVkIGNsdXN0ZXJzCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMgPC0gYygiI0ZDOTI3MiIsIiNFRjNCMkMiLGJyZXdlci5wYWwobj00LCJHcmVlbnMiKVsyOjRdLGJyZXdlci5wYWwobj00LCJZbE9yQnIiKVtjKDIsMyldLGJyZXdlci5wYWwobj02LCJCbHVlcyIpWzI6Nl0sYnJld2VyLnBhbChuPTYsIlB1cnBsZXMiKVsyOjZdLCJncmV5ODAiLCJncmV5ODAiLCJncmV5ODAiLCJncmV5ODAiKQogIApzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gdW5pcXVlKHN1YmxpbmVhZ2VzLmNvbHMuYnJld1ssYygiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCJzdWJsaW5lYWdlLmNvbHMiKV0pCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbb3JkZXIoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkpLF0Kc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlIDwtIGZhY3RvcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbIWlzLm5hKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSxdCgpjb2xuYW1lcyhzdWJsaW5lYWdlcy5jb2xzLmJyZXcpIDwtIGMoInN1YmxpbmVhZ2UiLCJzdWJsaW5lYWdlLmNvbHMiKQpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gdW5pcXVlKHN1YmxpbmVhZ2VzLmNvbHMuYnJldykKYGBgClwKUmVzdHJpY3QgYW5hbHlzaXMgdG8gaGlnaCBxdWFsaXR5IGdlbm9tZXMgKGFuZCB0cmVlKQpgYGB7cn0KVFBBLm1ldGEyLjEgPC0gVFBBLm1ldGEyLjFbVFBBLm1ldGEyLjEkZmluZXNjYWxlLmFuYWx5c2lzPT0iWWVzIixdCmBgYApcCkNyZWF0ZSBhICJVSyIgdmFyaWFibGUsIGFuZCBhICJQSEUiIHZhcmlhYmxlCmBgYHtyfQpUUEEubWV0YTIuMSRpcy5VSyA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkR2VvX0NvdW50cnk9PSJVSyIsIlVLIiwiT3RoZXIiKQpUUEEubWV0YTIuMSRpcy5QSEUgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJEdlb19Db3VudHJ5PT0iVUsiICYgZ3JlcGwoIlBIRSIsVFBBLm1ldGEyLjEkU2FtcGxlX05hbWUpLCJQSEUiLCJPdGhlciIpCmBgYApcCmBgYHtyfQojIFByZXBhcmUgTUwgdHJlZQpUUEEuTUx0cmVlLmdndHJlZSA8LSBnZ3RyZWUoVFBBLk1MdHJlZSxsYXlvdXQgPSAiZmFuIixvcGVuLmFuZ2xlID0gMTAsIHJpZ2h0PVQpCgojIFByZXBhcmUgY291bnRyeSBkYXRhc2V0ClRQQS5yYXdzZXEuY291bnRyaWVzLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIENvdW50cnk9VFBBLm1ldGEyLjEkR2VvX0NvdW50cnksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIGNvbnRpbmVudCBkYXRhc2V0ClRQQS5yYXdzZXEuY29udGluZW50cy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBDb250aW5lbnQ9VFBBLm1ldGEyLjEkQ29udGluZW50LCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgUHJlcGFyZSBVSyBkYXRhIHN0cmlwClRQQS5yYXdzZXEuVUsucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgRW5nbGFuZD1UUEEubWV0YTIuMSRpcy5VSywgc3RyaW5nc0FzRmFjdG9ycyA9IEYpClRQQS5yYXdzZXEuVUsucFtUUEEucmF3c2VxLlVLLnAkRW5nbGFuZD09IlVLIixdIDwtICJFbmdsYW5kIgoKIyBQcmVwYXJlIFBIRSBkYXRhIHN0cmlwClRQQS5yYXdzZXEuUEhFLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFBIRT1UUEEubWV0YTIuMSRpcy5QSEUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIE1ham9yIGxpbmVhZ2UgZGF0YXNldApUUEEucmF3c2VxLkxpbmVhZ2UucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgTGluZWFnZT1UUEEubWV0YTIuMSRUUEFfTGluZWFnZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgc3VibGluZWFnZSBsaW5lYWdlIGRhdGFzZXQKVFBBLnJhd3NlcS5zdWJMaW5lYWdlLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKCiMgUHJlcGFyZSBZZWFyIGRhdGFzZXQgKGFsbCBzYW1wbGVzKQpUUEEucmF3c2VxLmFsbC5ZZWFycy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBZZWFyPVRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCgpmbG9vcl81eWVhcnMgIDwtIGZ1bmN0aW9uKHZhbHVlKXsgcmV0dXJuKHZhbHVlIC0gdmFsdWUgJSUgNSkgfQpUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93IDwtIHBhc3RlMChmbG9vcl81eWVhcnMoYXMubnVtZXJpYyhUUEEubWV0YTIuMSRTYW1wbGVfWWVhcikpLCItIixmbG9vcl81eWVhcnMoYXMubnVtZXJpYyhUUEEubWV0YTIuMSRTYW1wbGVfWWVhcikpKzUpCiMgU29tZSBzYW1wbGVzIGhhdmUgdW5jZXJ0YWluIGRhdGVzICh1cCB0byAyMC0zMCB5ZWFycyB1bmNlcnRhaW50eSksIGJ1dCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoZXNlIHBsb3R0aW5nIGNhdGVnb3JpZXMgd2UnbGwgdXNlIHRoZSBjZW50cmVwb2ludCB5ZWFyClRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3cgPC0gc2FwcGx5KDE6bnJvdyhUUEEubWV0YTIuMSksIGZ1bmN0aW9uKHgpIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhclt4XT09Ii0iLE5BLCBpZmVsc2UoaXMubmEoVFBBLm1ldGEyLjEkU2FtcGxlXzV5ZWFyLndpbmRvd1t4XSksTkEsIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhclt4XT09IjE5NTAtMTk4MCIsIjE5NjUtMTk3MCIsaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iMTk2MC0xOTgwIiwiMTk2NS0xOTcwIiAsaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iMTk4MC0xOTk5IiwiMTk4NS0xOTkwIixUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93W3hdKSkpKSkpCgoKVFBBLm1ldGEyLjEkU2FtcGxlX3llYXIuMTk5MC5jdXR0b2ZmIDwtIGlmZWxzZShUUEEubWV0YTIuMSRTYW1wbGVfWWVhcj4xOTkwLFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyLCI8MTk5MCIpCgpUUEEubWV0YTIuMSRTYW1wbGVfeWVhci4xOTk5LmN1dHRvZmYgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyPjE5OTksVFBBLm1ldGEyLjEkU2FtcGxlX1llYXIsIjwxOTk5IikKVFBBLnJhd3NlcS55ZWFyLmN1dHRvZmYucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgU2FtcGxlLlllYXI9VFBBLm1ldGEyLjEkU2FtcGxlX3llYXIuMTk5OS5jdXR0b2ZmLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCmBgYApcClwKIyBCcmluZyBpbiBQSEUgbWV0YWRhdGEKYGBge3J9ClBIRS5tZXRhZGF0YS5saW5rZWQgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKFBIRS5tZXRhZGF0YS5saW5rZWQuZmlsZSkKYGBgClwKRG8gc29tZSBjbGVhbnVwIGFuZCBmYWN0b3Jpbmcgb2YgdmFyaWFibGVzCmBgYHtyfQoKUEhFLm1ldGFkYXRhLmxpbmtlZCRhZ2VfZ3JvdXAgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkYWdlX2dyb3VwLCBsZXZlbHM9cmV2KGMoIjE2LTI0IiwiMjUtMzQiLCIzNS00NCIsIjQ1KyIsIlVua25vd24iKSkpCgpQSEUubWV0YWRhdGEubGlua2VkJGxvbmRvbiA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRsb25kb24sbGV2ZWxzPXJldihjKCJZZXMiLCJObyIsIlVua25vd24iKSkpClBIRS5tZXRhZGF0YS5saW5rZWQkdWtib3JuIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJHVrYm9ybixsZXZlbHM9cmV2KGMoIlllcyIsIk5vIiwiVW5rbm93biIpKSkKUEhFLm1ldGFkYXRhLmxpbmtlZCRoaXZwb3MgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkaGl2cG9zLCBsZXZlbHM9cmV2KGMoIlllcyIsIk5vIiwiVW5rbm93biIpKSkKCiMgbmVlZCB0byB1cGRhdGUgdGVybWlub2xvZ3kgb2YgJ01TTScgdG8gJ0dCTVNNJwpQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uPT0iTVNNIiwiZ2VuZGVyX29yaWVudGF0aW9uIl0gPC0gIkdCTVNNIgpQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbiA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb24sIGxldmVscz1yZXYoYygiTVNXIiwiR0JNU00iLCJXU00iLCJNVW5rbm93biIsIlVua25vd24iKSkpCgpQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSwgbGV2ZWxzPXJldihjKCJFYXN0IE1pZGxhbmRzIiwgIkVhc3Qgb2YgRW5nbGFuZCIsICJMb25kb24iLCAiTm9ydGggRWFzdCIsICJOb3J0aCBXZXN0IiwgIlNvdXRoIEVhc3QiLCAiU291dGggV2VzdCIsICJXZXN0IE1pZGxhbmRzIiwgIllvcmtzaGlyZSBhbmQgSHVtYmVyIiwgIlVLIChub3QgRW5nbGFuZCkiLCAiTm90IEtub3duIikpKQoKUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSA8LSAgZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKCmBgYApcClwKIyMjIEV4dHJhY3QgaW5mb3JtYXRpb24gYWJvdXQgZHVwbGljYXRlcwpgYGB7cn0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFshaXMubmEoUEhFLm1ldGFkYXRhLmxpbmtlZCRkdXBfZmxhZyksXQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcyA8LSBQSEUubWV0YWRhdGEuZHVwbGljYXRlc1shaXMubmEoUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMkU2FtcGxlX05hbWUpLF0KCgpQSEUucGF0aWVudC5tYXRjaGVzIDwtIGRhdGEuZnJhbWUoCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHVwX2ZsYWcgPSBjKCIxQSIsIjFCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjJBIiwiMkIiLCIzQSIsIjNCIiwiNEEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNEIiLCI1QSIsIjVCIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHVwX1BhdGllbnQgPSBjKCJQYXRpZW50IDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCAxIiwiUGF0aWVudCAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGllbnQgMiIsIlBhdGllbnQgMyIsIlBhdGllbnQgMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDQiLCJQYXRpZW50IDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCA1IiwiUGF0aWVudCA1IiksCiAgICAgICAgICAgICAgICAgICAgICAgICBkdXBfUGF0aWVudF9TYW1wbGUgPSBjKCJzYW1wbGUgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMiIsInNhbXBsZSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAyIiwic2FtcGxlIDEiLCJzYW1wbGUgMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMSIsInNhbXBsZSAyIiwic2FtcGxlIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlIDIiKQogICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAKClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzIDwtIGxlZnRfam9pbihQSEUubWV0YWRhdGEuZHVwbGljYXRlcywgUEhFLnBhdGllbnQubWF0Y2hlcywgYnk9ImR1cF9mbGFnIikKClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzCmBgYAoKRHVwbGljYXRlIFNhbXBsZXMgbWlzc2luZyBtZXRhZGF0YSBhcmUgYWxsICduZXcgZHVwbGljYXRlcycgYW5kIHdlcmUgZXhjbHVkZWQgZHVlIHRvIGxvdyBtYXBwaW5nIGNvdmVyYWdlIChhbGwgY2hlY2tlZCkuClwKU2FtcGxlcyBsYWJlbGxlZCAnWkEnIGFuZCAnWEInIGhhZCBkdXBsaWNhdGVzIGluIHRoZSBvcmlnaW5hbCBkYXRhc2V0LCBidXQgdGhlIHJlY2lwcm9jYWwgcGFpcnMgd2VyZSBleGNsdWRlZCBkdWUgdG8gcXVhbGl0eSBpc3Vlcy4KXApBdmFpbGFibGUgcGFpcnMgLSBQYXRpZW50IDMsIFBhdGllbnQgNAoKYGBge3J9ClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZCA8LSBQSEUubWV0YWRhdGEuZHVwbGljYXRlc1tQSEUubWV0YWRhdGEuZHVwbGljYXRlcyRkdXBfUGF0aWVudCAlaW4lIGMoIlBhdGllbnQgMyIsIlBhdGllbnQgNCIpLF0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkW29yZGVyKFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLnBhaXJlZCRkdXBfUGF0aWVudCwgUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkJHllYXIsUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkJG1vbnRoKSxjKCJTYW1wbGVfTmFtZSIsImR1cF9QYXRpZW50IiwgIm1vbnRoLmZpeCIsICJ5ZWFyIildCmBgYApcClRoZXNlIHdpbGwgYmUgcmV2aXNpdGVkIGxhdGVyIGluIHRoZSBhbmFseXNpcy4gClwKUGF0aWVudCA0CkhJVi12ZSBNU00gKDQ1KyksIFVLIGJvcm4sIFBIRSByZWdpb24gRAoyIHNhbXBsZXMsIGNvbGxlY3RlZCBpbiB0aGUgc2FtZSBtb250aCBhbmQgeWVhcgpCb3RoIHNhbXBsZXMgYXJlIHN1YmxpbmVhZ2UgMSwgYW5kIGlkZW50aWNhbCAoMCBwd1NOUHMpCkxpa2VseSB0aGUgc2FtZSBpbmZlY3Rpb24gKGRlcGVuZGluZyBvbiBkYXRlcywgdHJlYXRtZW50LCBldGMpLCBidXQgY2Fu4oCZdCBydWxlIG91dCByZWluZmVjdGlvbiB3aXRoIHNhbWUgc3RyYWluLgpcClBhdGllbnQgMwpISVYtdmUgTVNNICgzNS00NCksIG5vdCBVSyBib3JuLCBiYXNlZCBpbiBMb25kb24gKEMpCjIgc2FtcGxlcywgY29sbGVjdGVkIDkgbW9udGhzIGFwYXJ0CkJvdGggc2FtcGxlcyBhcmUgc3VibGluZWFnZSAxLCBidXQgaGF2ZSA3IHBhaXJ3aXNlIFNOUHMgYmV0d2VlbiB0aGVtIChsb2FkcyEpClJlaW5mZWN0aW9uIOKAkyBwcm9iYWJseSBmcm9tIGEgZGlmZmVyZW50IHRyYW5zbWlzc2lvbiBuZXR3b3JrClwKXApIb3dldmVyLCBiYXNlZCBvbiB0aGUgc2FtcGxlIGRhdGVzLCBhcyB3ZWxsIGFzIHRoZSBvdXRjb21lIG9mIHRoZSBkb3duc3RyZWFtIGdlbmV0aWMgYW5hbHlzaXMsIHdlIGNhbiBzZWUgdGhhdCBQYXRpZW50IDMgaGFzIGR1cGxpY2F0ZSBpbmZlY3Rpb24gZXZlbnRzIChkaWZmZXJlbnQgZGF0ZXMsIDEwIG1vbnRocyBhcGFydCkgYW5kIHRoZSBnZW5vbWVzIGFyZSBkaXN0aW5jdCAoNyBTTlBzIGFwYXJ0KSwgd2hlcmVhcyBQYXRpZW50IDQgc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZCBpbiB0aGUgc2FtZSBtb250aCBhbmQgeWVhciAoaS5lLiBhcmUgbGlrZWx5IGR1cGxpY2F0ZXMgZnJvbSB0aGUgc2FtZSBpbmZlY3Rpb24pIGFuZCBoYXMgaWRlbnRpY2FsIGdlbm9tZXMuClwKRm9yIGRvd25zdHJlYW0gYW5hbHlzaXMgcHVycG9zZXMsIHdlIHdpbGwgcmV0YWluIGJvdGggc2FtcGxlcyBmb3IgUGF0aWVudCAzIChkaXNjcmV0ZSBpbmZlY3Rpb25zKSwgYnV0IGV4Y2x1ZGUgb25lIHNhbXBsZSBmcm9tIFBhdGllbnQgNCAoZHVwbGljYXRlIGluZmVjdGlvbiBzYW1wbGVzKSAtICdQSEUxNTAxMjZBJyBoYXMgbXVjaCBiZXR0ZXIgZ2Vub21lIGNvdmVyYWdlLCBzbyBleGNsdWRlICdQSEUxNTAxMjVBJwpcClwKIyMjIEZ1cnRoZXIgRXhjbHVzaW9ucyBcClBIRTEzMDA1NkEgLSBkdXBsaWNhdGUgb2YgUEhFMTMwMDU3QiAoYWxyZWFkeSByZW1vdmVkLCBzbyBub3QgcmVsZXZhbnQpIC0gZG9uJ3QgZXhjbHVkZSEKUEhFMTcwNDAyQSAtIHF1YWxpdHkgY29udHJvbCBzYW1wbGUKUEhFMTcwMzc4QSAtIHF1YWxpdHkgY29udHJvbCBzYW1wbGUKClwKRXhjbHVkZSBkdXBsaWNhdGUgc2VxdWVuY2VzCmBgYHtyfQpkdXBsaWNhdGUuZXhjbHVzaW9uLmxpc3QgPC0gYygiUEhFMTUwMTI1QSIsIlBIRTE3MDQwMkEiLCJQSEUxNzAzNzhBIikKUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSBQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUgJW5vdGluJSBkdXBsaWNhdGUuZXhjbHVzaW9uLmxpc3QsXQpgYGAKXAoKIyMjIE1vdmluZyBvbi4uLiBcCgpEZWZpbmUgc29tZSBjb2xvdXIgc2NoZW1lcwpgYGB7cn0KIyBkZWZpbmUgc29tZSBjb2xvcnMgZm9yIGVhY2ggcmVnaW9uClBIRS5yZWdpb24uY29scy5icmV3IDwtIGRhdGEuZnJhbWUoVUtIU0EucmVnaW9uPWMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIsICJFYXN0IE1pZGxhbmRzIiwgIldlc3QgTWlkbGFuZHMiLCAiRWFzdCBvZiBFbmdsYW5kIiwgIkxvbmRvbiIsICJTb3V0aCBFYXN0IiwiU291dGggV2VzdCIsIlVLIChub3QgRW5nbGFuZCkiLCAiTm90IEtub3duIiksIHN0cmluZ3NBc0ZhY3RvcnM9RikKUEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCA8LSBjKCIjQTZDRUUzIiwiIzFGNzhCNCIsIiNDQUIyRDYiLCIjMzNBMDJDIiwiI0IyREY4QSIsIiNGRjdGMDAiLCIjRTMxQTFDIiwiI0ZCOUE5OSIsIiNENEJCMDIiLCJncmV5NzUiLCJncmV5MjUiKQoKIyBISVYgY29sb3Igc2NoZW1lClBIRS5oaXYuY29scyA8LSBkYXRhLmZyYW1lKGhpdnBvcz1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRoaXZwb3MpKSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKUEhFLmhpdi5jb2xzJGhpdi5jb2xzIDwtIGMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiZ3JleTc1IikKCiMgT3JpZW50YXRpb24gY29sb3VyIHNjaGVtZQpQSEUub3JpZW50YXRpb24uY29scyA8LSBkYXRhLmZyYW1lKG9yaWVudGF0aW9uPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbikpKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQpQSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbiA8LSBmYWN0b3IoUEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24sIGxldmVscz1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb24pKSksIGxhYmVscz1jKCJNU1ciLCJHQk1TTSIsIldTTSIsIk1Vbmtub3duIiwiVW5rbm93biIpKQpQSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzIDwtIGMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiI2ZiOWE5OSIsIiNhNmNlZTMiLCJncmV5NzUiKQoKIyBVSyBib3JuIGNvbG91ciBzY2hlbWUKUEhFLnVrYm9ybi5jb2xzIDwtIGRhdGEuZnJhbWUodWtib3JuPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJHVrYm9ybikpKSx1a2Jvcm4uY29scz1jKCIjMWY3OGI0IiwiI2IyZGY4YSIsImdyZXk3NSIpLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBMb25kb24gYmFzZWQgY29sb3VyIHNjaGVtZQpQSEUubG9uZG9uLmNvbHMgPC0gZGF0YS5mcmFtZShsb25kb249cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkbG9uZG9uKSkpLGxvbmRvbi5jb2xzPWMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiZ3JleTc1Iiksc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgoKIyBBZ2UgZ3JvdXAgY29sb3VyIHNjaGVtZQpQSEUuQWdlLmNvbHMgPC0gZGF0YS5mcmFtZShhZ2VfZ3JvdXA9cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkYWdlX2dyb3VwKSkpLHN0cmluZ3NBc0ZhY3RvcnMgPSBUKQpQSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMgPC0gYyhicmV3ZXIucGFsKG49NCwiWWxHbkJ1IiksImdyZXk3NSIpCgojIFNhbXBsZSBEYXRlIGNvbG91ciBzY2hlbWUKUEhFLnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkeWVhcikpKSxzdHJpbmdzQXNGYWN0b3JzID0gVCkKUEhFLnllYXIuY29scyR5ZWFyLmNvbHMgPC0gYnJld2VyLnBhbChuPTcsIllsT3JSZCIpCgojIFNhbXBsZSBEYXRlIChhbGwgZ2xvYmFsIGRhdGEsIGJ1dCB3aXRoIDE5OTAgY3V0dG9mZikKVFBBLnllYXIuY3V0dG9mZi5jb2xzIDwtIGRhdGEuZnJhbWUoZGF0ZS5jdXR0b2ZmPWMoIjwxOTk5IiwxOTk5OjIwMTkpLCBkYXRlLmN1dHRvZmYuY29sPWMoIiNGMkYyRjIiLGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg3LCAiWWxPclJkIikpKGxlbmd0aCgxOTk5OjIwMTkpKSkpCgoKYGBgClwKXAojIyMjIwojIyBGaXJzdCBkZXNjcmliZSB0aGUgc2VxdWVuY2VkIHBvcHVsYXRpb24gYXMgYSB3aG9sZQpcClNldCBvcmRlciBvZiBQSEUgcmVnaW9ucwpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUsIGxldmVscz1yZXYoUEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSkKYGBgClwKR2VuZXJhdGUgc29tZSBiYXNpYyBzdGF0aXN0aWNzIGFib3V0IGdlb2dyYXBoaWNhbCBQSEUgcmVnaW9ucyAoYW5vbnltaXNlZCkKYGBge3J9ClBIRS5jb3VudC5hbGwgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uPW4oKSkKClBIRS5jb3VudC55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIueWVhcj1uKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKHBlcmMucGVyLnllYXI9KGNvdW50LnBlci55ZWFyL3N1bShjb3VudC5wZXIueWVhcikpKjEwMCkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgc3RhdHVzClBIRS5ISVYuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbikgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKEhJVi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBnZW5kZXIgb3JpZW50YXRpb24KUEhFLm9yaWVudGF0aW9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhnZW5kZXJfb3JpZW50YXRpb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lIAogIGRwbHlyOjptdXRhdGUob3JpZW50YXRpb24ucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4gKHZhZ3VlIGNhdGVnb3J5IHRoYXQncyB1bmZvcnR1bmF0ZWx5IG9ubHkgbWFyZ2luYWxseSBoZWxwZnVsKQpQSEUuVUtib3JuLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkodWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh1a2Jvcm4pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lIAogIGRwbHlyOjptdXRhdGUoVUtib3JuLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExvbmRvbiBiYXNlZApQSEUuTG9uZG9uLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkobG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShMb25kb24ucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEFnZSBncm91cApQSEUuQWdlLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShBZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExpbmVhZ2UgZ3JvdXAKUEhFLkxpbmVhZ2UuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEFfTGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBX0xpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShMaW5lYWdlLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwClBIRS5zdWJsaW5lYWdlLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoU3VibGluZWFnZS5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCmBgYApcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9CiMgTWFrZSBoYmFyIHBsb3Qgb2Ygc2FtcGxlIGNvdW50cyBieSByZWdpb24KcC5hbGwuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5jb3VudC5hbGwsIGFlcyh4PWNvdW50LnBlci5yZWdpb24seT0iIikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0ic3RhY2siLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0iZ3JleTMwIikgKyAKICBnZW9tX3RleHQoZGF0YT1QSEUuY291bnQuYWxsLCBhZXMoKGNvdW50LnBlci5yZWdpb24rMTIpLCAiIixsYWJlbD1jb3VudC5wZXIucmVnaW9uKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBsYWJzKHk9IkFsbCIsIHg9IlNhbXBsZSBDb3VudCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTQpKSAKI3AuYWxsLmhiYXJwbG90CgojIG1ha2UgdGVtcG9yYWwgYnViYmxlcGxvdCBvZiBjb3VudHMgYnkgcmVnaW9uCnAuYWxsLnllYXIuYnViYmxlcGxvdCA8LSBnZ3Bsb3QoUEhFLmNvdW50LnllYXJzLCBhZXMoYXMubnVtZXJpYyh5ZWFyKSwgeT0iQWxsIikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPWNvdW50LnBlci55ZWFyKSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5MzAiKSArIAogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBsYWJzKHk9IiIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3AuYWxsLnllYXIuYnViYmxlcGxvdAoKIyBNYWtlIHByb3BvcnRpb25hbCBoYmFyIHBsb3Qgb2YgSElWIHN0YXR1cwpwLmFsbC5oaXYuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5ISVYuY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPWhpdnBvcykpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iSElWICt2ZSIsdmFsdWVzPVBIRS5oaXYuY29scyRoaXYuY29scywgYnJlYWtzPVBIRS5oaXYuY29scyRoaXZwb3MpICsKICBsYWJzKHk9IkFsbCIsIHg9IkhJViArdmUiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuSElWLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKI3AuYWxsLmhpdi5oYmFycGxvdAoKcC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9Z2VuZGVyX29yaWVudGF0aW9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJPcmllbnRhdGlvbiIsdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikgKwogIGxhYnMoeT0iQWxsIiwgeD0iT3JpZW50YXRpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUub3JpZW50YXRpb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QKCnAuYWxsLnVrYm9ybi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLlVLYm9ybi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9dWtib3JuKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS1xuQm9ybiIsdmFsdWVzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4uY29scywgYnJlYWtzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4pICsKICBsYWJzKHk9IkFsbCIsIHg9IlVLIEJvcm4iKSArCiAgI2d1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLlVLYm9ybi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLmFsbC51a2Jvcm4uaGJhcnBsb3QKCnAuYWxsLkxvbmRvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLkxvbmRvbi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9bG9uZG9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJMb25kb24iLHZhbHVlcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uLmNvbHMsIGJyZWFrcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uKSArCiAgbGFicyh5PSJBbGwiLCB4PSJMb25kb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuTG9uZG9uLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLkxvbmRvbi5oYmFycGxvdAoKcC5hbGwuQWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuQWdlLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1hZ2VfZ3JvdXApKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJBbGwiLCB4PSJBZ2UgR3JvdXAiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuQWdlLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLkFnZS5oYmFycGxvdApgYGAKXApQbG90IGNvbWJpbmVkIHBsb3QgZm9yICdhbGwgc2FtcGxlcycKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0KUEhFLmFsbC5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90LCBwLmFsbC5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5hbGwuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5hbGwuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgpQSEUuYWxsLmNvbWJpcGxvdC4xCmBgYApcClwKTmV4dCBqdXN0IGRlc2NyaWJlIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9ucyBieSBQSEUgcmVnaW9uCmBgYHtyfQojIGdlbmVyYXRlIHNvbWUgYmFzaWMgc3RhdGlzdGljcyBhYm91dCBnZW9ncmFwaGljYWwgUEhFIHJlZ2lvbnMgKGFub255bWlzZWQpClBIRS5nZW8uY291bnQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKGNvdW50LnBlci5yZWdpb24pLGZyYWN0aW9uPWNvdW50LnBlci5yZWdpb24vdG90YWwuY291bnQpCgpQSEUuZ2VvLmNvdW50LnllYXJzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi55ZWFyPW4oKSkKClBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSx5ZWFyLFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24ueWVhcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQueWVhcj1zdW0oY291bnQucGVyLnJlZ2lvbi55ZWFyKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1UUEFfTGluZWFnZSwgdmFsdWVzX2Zyb20gPSBjb3VudC5wZXIucmVnaW9uLnllYXIpClBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZVtpcy5uYShQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UpXSA8LSAwClBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSR5ZWFyIDwtIGFzLm51bWVyaWMoUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlJHllYXIpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgSElWIHN0YXR1cwpQSEUuZ2VvLkhJVi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24uaGl2PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKGNvdW50LnBlci5yZWdpb24uaGl2KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1jb3VudC5wZXIucmVnaW9uLmhpdi90b3RhbC5yZWdpb24pICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoaGl2cG9zKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0ID0gY3Vtc3VtKGZyYWN0aW9uKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgRG91YmxlIENoZWNrIEhJViBzdGF0dXMgZGF0YSBmb3Igbm9uLVBIRSBkYXRhc2V0IC0gY29uZmlybWVkIG5vIEhJVit2ZXMgZnJvbSBub24tTVNNLiAKUEhFLnNvdXJjZWxhYi5ISVYuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShpcy5QSEUsIGdlbmRlcl9vcmllbnRhdGlvbiwgaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5vcmllbnRhdGlvbi5oaXY9bigpKSAjJT4lCiAgI2RwbHlyOjpmaWx0ZXIoaXMuUEhFIT0iUEhFIikKCiMgR2V0IHRvdGFsIHBvcHVsYXRpb24gc3RhdHMgZm9yIEhJVgpQSEUuYWxsLkhJVi5jb3VudHMgPC0gIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQuaGl2PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC50b3RhbD1zdW0oY291bnQuaGl2KSwgZnJhY3Rpb249Y291bnQuaGl2L2NvdW50LnRvdGFsKQoKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUub3JpZW50YXRpb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2Uob3JpZW50YXRpb24uY291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKG9yaWVudGF0aW9uLnBlcmNlbnQ9KG9yaWVudGF0aW9uLmNvdW50L3N1bShvcmllbnRhdGlvbi5jb3VudCkqMTAwKSkKClBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24pKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGdlbmRlcl9vcmllbnRhdGlvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPWNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24vdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JSAKICBkcGx5cjo6bXV0YXRlKG9yaWVudGF0aW9uLnBlcmNlbnQ9KGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24vc3VtKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24pKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgVUsgYm9ybgpQSEUuZ2VvLlVLYm9ybiA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgdWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh1a2Jvcm4pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IExvbmRvbiBiYXNlZApQSEUuZ2VvLkxvbmRvbiA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgbG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLmdlby5BZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIGFnZV9ncm91cCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoYWdlX2dyb3VwKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTGluZWFnZSBncm91cApQSEUuZ2VvLkxpbmVhZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEFfTGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IHN1YmxpbmVhZ2UgZ3JvdXAKUEhFLmdlby5zdWJsaW5lYWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKYGBgClwKTWFrZSBzb21lIHBsb3RzCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KIyBNYWtlIGhiYXIgcGxvdCBvZiBzYW1wbGUgY291bnRzIGJ5IHJlZ2lvbgpwLnJlZ2lvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5jb3VudCwgYWVzKGNvdW50LnBlci5yZWdpb24scGhlX2NlbnRyZSwgZmlsbD1waGVfY2VudHJlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJzdGFjayIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIix2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uY291bnQsIGFlcygoY291bnQucGVyLnJlZ2lvbisxMiksIHBoZV9jZW50cmUsbGFiZWw9Y291bnQucGVyLnJlZ2lvbiksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJTYW1wbGUgQ291bnQiKSArCiAgI2Nvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwxMzApKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDI2MCkpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgCiNwLnJlZ2lvbi5oYmFycGxvdAoKIyBtYWtlIHRlbXBvcmFsIGJ1YmJsZXBsb3Qgb2YgY291bnRzIGJ5IHJlZ2lvbgpwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uY291bnQueWVhcnMsIGFlcyhhcy5udW1lcmljKHllYXIpLCBwaGVfY2VudHJlLCBjb2xvdXI9cGhlX2NlbnRyZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPWNvdW50LnBlci5yZWdpb24ueWVhcikpICsgCiAgZ2VvbV9saW5lKGFscGhhPTAuMjUpICsKICBndWlkZXMoY29sb3VyPSdub25lJykgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDcsYnJlYWtzPWMoMSw1LDEwLDI1LDUwKSkgKyAKICBndWlkZXMoc2l6ZT1ndWlkZV9sZWdlbmQobnJvdz0yLCBkaXJlY3Rpb24gPSAnaG9yaXpvbnRhbCcsIGJ5cm93PVQpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJTYW1wbGUgWWVhciIsIHNpemU9IkNvdW50IikgCiNwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QKCiMgT3IgYSBiYXJwbG90IG9mIGxpbmVhZ2UgYnkgeWVhciAmIFBIRSByZWdpb24/CnAucmVnaW9uLnllYXIuYnViYmxlcGxvdC5iYXJwbG90LmZhY2V0LmxpbmVhZ2UgPC0gUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlICU+JSB0aWR5cjo6cGl2b3RfbG9uZ2VyKGMoU1MxNCwgTmljaG9scyksIG5hbWVzX3RvPSJUUEFfTGluZWFnZSIsIHZhbHVlc190bz0iQ291bnQiKSAlPiUKICBnZ3Bsb3QoYWVzKHllYXIsIENvdW50LCBmaWxsPVRQQV9MaW5lYWdlKSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoPTAuNikgKyAKICBmYWNldF9ncmlkKHBoZV9jZW50cmV+Liwgc2NhbGVzPSdmcmVlJykgKwogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5MaW5lYWdlIix2YWx1ZXM9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCwgYnJlYWtzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSkgKwogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9J3doaXRlJywgZmlsbD0nd2hpdGUnLGxpbmV0eXBlPSJzb2xpZCIpLCBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1Iiwgc2l6ZT03LCBhbmdsZT0wKSkgCiNwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QuYmFycGxvdC5mYWNldC5saW5lYWdlCgojIE1ha2UgcHJvcG9ydGlvbmFsIGhiYXIgcGxvdCBvZiBISVYgc3RhdHVzCnAucmVnaW9uLmhpdi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5ISVYuY291bnRzLCBhZXMoY291bnQucGVyLnJlZ2lvbi5oaXYscGhlX2NlbnRyZSxmaWxsPWhpdnBvcykpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJISVYgK3ZlIix2YWx1ZXM9UEhFLmhpdi5jb2xzJGhpdi5jb2xzLCBicmVha3M9UEhFLmhpdi5jb2xzJGhpdnBvcykgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uSElWLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Y291bnQucGVyLnJlZ2lvbi5oaXYpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKI3AucmVnaW9uLmhpdi5oYmFycGxvdAoKcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzLCBhZXMoY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbixwaGVfY2VudHJlLGZpbGw9Z2VuZGVyX29yaWVudGF0aW9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik9yaWVudGF0aW9uIix2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJPcmllbnRhdGlvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1jb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdAoKcC5yZWdpb24udWtib3JuLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLlVLYm9ybiwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD11a2Jvcm4pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUsgQm9ybiIsdmFsdWVzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4uY29scywgYnJlYWtzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4pICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlVLIEJvcm4iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLlVLYm9ybiwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLnVrYm9ybi5oYmFycGxvdAoKcC5yZWdpb24uTG9uZG9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkxvbmRvbiwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1sb25kb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTG9uZG9uIix2YWx1ZXM9UEhFLmxvbmRvbi5jb2xzJGxvbmRvbi5jb2xzLCBicmVha3M9UEhFLmxvbmRvbi5jb2xzJGxvbmRvbikgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iTG9uZG9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5Mb25kb24sIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnJlZ2lvbi5Mb25kb24uaGJhcnBsb3QKCnAucmVnaW9uLkFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5BZ2UsIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9YWdlX2dyb3VwKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJBZ2UgR3JvdXAiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLkFnZS5oYmFycGxvdApgYGAKXApDb21iaW5lZCBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9ClBIRS5yZWdpb24uY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCwgcC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMQpgYGAKCgpcClJlZ2lvbnMgYXMgYSBjb21wbGV4IG11bHRpcGFuZWwgcGxvdApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTQuNX0KCgojIGxlZ2VuZHMKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMS5sZWdlbmRzIDwtIHBsb3RfZ3JpZChnZXRfbGVnZW5kKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCksIGdldF9sZWdlbmQocC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAucmVnaW9uLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAucmVnaW9uLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsNCw0LDQsNCksIHNjYWxlPTAuOTUpCgoKIyBBcnJhbmdlIHBsb3RzIHZlcnRpY2FsbHkKcC55ZWFyLmJ1YmJsZXBsb3QuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLnllYXIuYnViYmxlcGxvdCArIHgudGhlbWUuc3RyaXAsIHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLmNvdW50cy5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIub3JpZW50YXRpb24uY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLmhpdi5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuaGl2LmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5yZWdpb24uaGJhci5BZ2UuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLkFnZS5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCiMgQ29tYmluZSB0aGUgcGxvdHMKcC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbCA8LSBwbG90X2dyaWQocC55ZWFyLmJ1YmJsZXBsb3QuY29tYmksIHAucmVnaW9uLmhiYXIuY291bnRzLmNvbWJpLCBwLnJlZ2lvbi5oYmFyLm9yaWVudGF0aW9uLmNvbWJpLCBwLnJlZ2lvbi5oYmFyLmhpdi5jb21iaSwgcC5yZWdpb24uaGJhci5BZ2UuY29tYmksIG5yb3c9MSwgcmVsX3dpZHRocz1jKDYsNCw0LDQsNCksIGxhYmVscyA9IGMoIkEiLCJCIiwiQyIsIkQiLCJFIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHZqdXN0PTAuMjUpCiMgYW5kIGFkZCB0aGUgbGVnZW5kcyBvbiB0b3AKcC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKHAucmVnaW9uLmhiYXIuY29tYmkucGx1cy5hbGwsIFBIRS5yZWdpb24uY29tYmlwbG90LjEubGVnZW5kcywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDYsMSksIHNjYWxlID0gMC45NSkKCgoKcC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWcyX1RQQS1QSEVfU2FtcGxlLW1ldGFkaXN0cm9zLWJ5LXBoZV9yZWdpb24rYWxsLWNvbWJpLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTI0MCwgaGVpZ2h0PTEzNSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYApcClwKTm93IGxldHMgbG9vayBhdCBzb21lIGdlbmV0aWMgZGF0YQpcCiMjIyBNYWtlIE1MIHRyZWUgd2l0aCBzdWJsaW5lYWdlIHRpcHBvaW50cwpgYGB7cn0KVFBBLk1MdHJlZS5nZ3RyZWUudGlwcG9pbnQgPC0gVFBBLk1MdHJlZS5nZ3RyZWUgJTwrJSBkYXRhLmZyYW1lKFNhbXBsZV9OYW1lPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSArIAogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPVN1YmxpbmVhZ2UpLCBzaXplPTAuNSwgYWxwaGE9MC41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJTdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKQpgYGAKXApBZGQgbWV0YWRhdGEKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KIyBDb250aW5lbnQKcC5UUEEuTUx0cmVlLlBIRSA8LSBnaGVhdG1hcChUUEEuTUx0cmVlLmdndHJlZS50aXBwb2ludCwKICAgICAgICAgICAgICAgVFBBLnJhd3NlcS5jb250aW5lbnRzLnAsIGNvbG9yPU5VTEwsd2lkdGg9MC4wNzUsb2Zmc2V0PTAuMDAwMDAwMjUsIGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkNvbnRpbmVudCIsdmFsdWVzPWNvbnRpbmVudGFsLmNvbHMuYnJldzIkY29udGluZW50LmNvbCwgYnJlYWtzPWNvbnRpbmVudGFsLmNvbHMuYnJldzIkQ29udGluZW50LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsbmNvbD0yKSkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgbmV3X3NjYWxlX2ZpbGwoKQoKIyBpcyBVSwpwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKHAuVFBBLk1MdHJlZS5QSEUsCiAgICAgICAgICAgICAgIFRQQS5yYXdzZXEuVUsucCwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMTAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iRW5nbGFuZC9PdGhlciIsIHZhbHVlcz1jKCJibGFjayIsImdyZXk5NSIpLCBicmVha3M9YygiRW5nbGFuZCIsIk90aGVyIiksIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMixuY29sPTIpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBuZXdfc2NhbGVfZmlsbCgpCgojIExpbmVhZ2UKcC5UUEEuTUx0cmVlLlBIRSA8LSBnaGVhdG1hcChwLlRQQS5NTHRyZWUuUEhFLFRQQS5yYXdzZXEuTGluZWFnZS5wLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAyMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJMaW5lYWdlIix2YWx1ZXM9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLmNvbCwgYnJlYWtzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAzLCBuY29sPTIpKSArIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgIG5ld19zY2FsZV9maWxsKCkgKwogIE5VTEwKCiMgc3VibGluZWFnZQpwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKHAuVFBBLk1MdHJlZS5QSEUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMzAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA0LCBuY29sPTMpKSArIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgIG5ld19zY2FsZV9maWxsKCkgKwogIE5VTEwKYGBgClwKcGxvdApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwLlRQQS5NTHRyZWUuUEhFCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwgIlN1cEZpZzNfVFBBLVBIRV9HbG9iYWxfUGh5bG8rVUstaGlnaGxpZ2h0cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0xNjAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYApcClwKIyMjIEdlb2dyYXBoaWMgZGlzdHJpYnV0aW9ucyBvZiBMaW5lYWdlcyBhbmQgU3VibGluZWFnZXMKV2hhdCBhYm91dCBzdWJsaW5lYWdlcz8KYGBge3J9CnAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uTGluZWFnZSwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1UUEFfTGluZWFnZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcbkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJUUEEgTGluZWFnZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogICNnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkxpbmVhZ2UsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLnN1YmxpbmVhZ2UsIGFlcyhDb3VudCxwaGVfY2VudHJlLGZpbGw9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJUUEEgU3VibGluZWFnZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz00KSkgKwogICNnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLnN1YmxpbmVhZ2UsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpgYGAKXApDb21iaSBwbG90IChnZW9ncmFwaHkgbGluZWFnZXMpCmBgYHtyfQpQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzIDwtIHBsb3RfZ3JpZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QgK2xlZ2VuZC5zdHJpcCwgcC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwICsgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDE1MCkpLCBwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArbGVnZW5kLnN0cmlwLCBwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArbGVnZW5kLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsMyw0LDQpLCBzY2FsZT0wLjk5LCBsYWJlbHM9YygiQyIsIkQiLCJFIiwiRiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKIyBzZXBhcmF0ZSBvdXQgdGhlIHBsb3QgZm9yIHRoZSBsZWdlbmRzCnAucmVnaW9uLnllYXIuYnViYmxlcGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QpCnAucmVnaW9uLmhiYXJwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCkKcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCkKcC5yZWdpb24uc3VibGluZWFnZS5oYmFycGxvdC5sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCkKClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMubGVnZW5kIDwtIHBsb3RfZ3JpZChwLnJlZ2lvbi55ZWFyLmJ1YmJsZXBsb3QubGVnZW5kLCBwLnJlZ2lvbi5oYmFycGxvdC5sZWdlbmQsIHAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QubGVnZW5kLCBwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg2LDMsNCw0KSkKClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMgPC0gcGxvdF9ncmlkKFBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMsIFBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMubGVnZW5kLCByZWxfaGVpZ2h0cyA9IGMoNCwxKSwgbmNvbD0xKQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcwpgYGAKXApPSywgbGV0J3Mgbm93IGFkZCBhIG1hcCBvZiB0aGVzZSBnZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9ucwoKXApMZXQncyB1c2VkIE9OUyBwdWJsaXNoZWQgc2hhcGUgZmlsZXMgLSB0aGVyZSBpcyBvbmUgYXZhaWxhYmxlIHRoYXQgc2hvd3MgUHVibGljIEhlYWx0aCBFbmdsYW5kIHJlZ2lvbiBib3VuZGFyaWVzLiAKYGBge3J9CgojIEdlbmVyYXRlIGFwcHJveGltYXRlIHJlZ2lvbmFsIEdQUyBjb29yZHMKUEhFLnJlZ2lvbi5HUFMgPC0gZGF0YS5mcmFtZSgKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICBwaGVfY2VudHJlID0gYygiRWFzdCBNaWRsYW5kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiRWFzdCBvZiBFbmdsYW5kIiwiTG9uZG9uIiwiTm9ydGggRWFzdCIsIk5vcnRoIFdlc3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgIlNvdXRoIEVhc3QiLCJTb3V0aCBXZXN0IiwiV2VzdCBNaWRsYW5kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiLCJVSyAobm90IEVuZ2xhbmQpIiwiTm90IEtub3duIiksCiAgICAgICAgICAgIExvbmdpdHVkZSA9IGMoLTAuNywwLjUsLTAuMiwtMS45LC0yLjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAwLjA1LC0yLjksLTIsLTAuOCwwLjEsMC42MyksCiAgICAgICAgICAgTGF0aXR1ZGUgPSBjKDUyLjksNTIuNCw1MS41LDU1LDUzLjcsCiAgICAgICAgICAgICAgICAgICAgICAgICA1MS4xLDUxLDUyLjYsNTMuOCw1NC43LDU0LjEpCiAgKSAgClBIRS5yZWdpb24uR1BTIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgUEhFLmdlby5MaW5lYWdlW1BIRS5nZW8uTGluZWFnZSRUUEFfTGluZWFnZT09IlNTMTQiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUylbNF0gPC0gIlNTMTQiClBIRS5yZWdpb24uR1BTIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgUEhFLmdlby5MaW5lYWdlW1BIRS5nZW8uTGluZWFnZSRUUEFfTGluZWFnZT09Ik5pY2hvbHMiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUylbNV0gPC0gIk5pY2hvbHMiClBIRS5yZWdpb24uR1BTW2lzLm5hKFBIRS5yZWdpb24uR1BTKV0gPC0gMAoKUEhFLnJlZ2lvbi5HUFMgPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBQSEUuZ2VvLkxpbmVhZ2VbUEhFLmdlby5MaW5lYWdlJFRQQV9MaW5lYWdlPT0iU1MxNCIsYygicGhlX2NlbnRyZSIsInRvdGFsLnJlZ2lvbiIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUylbNl0gPC0gIlJlZ2lvbl9Db3VudCIKClBIRS5yZWdpb24uR1BTJHJhZGl1cyA8LSAwLjUqKDEtMS9zcXJ0KFBIRS5yZWdpb24uR1BTJFJlZ2lvbl9Db3VudCkpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEltcG9ydCBkYXRhZmlsZSBmcm9tIGh0dHBzOi8vZ2VvcG9ydGFsLnN0YXRpc3RpY3MuZ292LnVrL2RhdGFzZXRzL3B1YmxpYy1oZWFsdGgtZW5nbGFuZC1jZW50cmVzLWRlY2VtYmVyLTIwMTYtZnVsbC1jbGlwcGVkLWJvdW5kYXJpZXMtaW4tZW5nbGFuZC9leHBsb3JlP2xvY2F0aW9uPTUyLjk1MDAwMCUyQy0yLjAwMDAwMCUyQzYuODgKClVLLnNoYXBlZmlsZSA8LSByZWFkT0dSKGRzbj1VSy5wdWJsaWNoZWFsdGguc2hhcGVmaWxlLmRhdGEpCgojUmVzaGFwZSBmb3IgZ2dwbG90MiB1c2luZyB0aGUgQnJvb20gcGFja2FnZQojVUsubWFwZGF0YSA8LSB0aWR5KFVLLnNoYXBlZmlsZSwgcmVnaW9uPSJwaGVjMTZubSIpClVLLm1hcGRhdGEgPC0gdGlkeShVSy5zaGFwZWZpbGUpClVLLm1hcGRhdGEuY29kZXMgPC0gZGF0YS5mcmFtZShzdF9hc19zZihVSy5zaGFwZWZpbGUsIGdyb3VwPSJwaGVjMTZubSIpKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJpZCIpICU+JQogIHNlbGVjdChpZCwgcGhlYzE2bm0pCgpVSy5tYXBkYXRhIDwtIFVLLm1hcGRhdGEgJT4lIGxlZnRfam9pbihVSy5tYXBkYXRhLmNvZGVzLCBieT0naWQnKSAlPiUKICBtdXRhdGUoaWQ9cGhlYzE2bm0pCgojVUsuZ2cgPC0gZ2dwbG90KCkgKyBnZW9tX3BvbHlnb24oZGF0YSA9IFVLLm1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGNvbG9yID0gIiNGRkZGRkYiLCBzaXplID0gMC4yNSkKVUsuZ2cgPC0gZ2dwbG90KCkgKyBnZW9tX3BvbHlnb24oZGF0YSA9IFVLLm1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGNvbG9yPSJncmV5MjUiLCBmaWxsPSJncmV5OTAiLCBzaXplID0gMC4wNzUpCgojVUsuZ2cgPC0gVUsuZ2cgKyBjb29yZF9maXhlZCgxKSArIHRoZW1lX25vdGhpbmcoKQojVUsuZ2cKIyBNYXAgcGxvdHRpbmcgZmlsZSBiZWNvbWVzIF92ZXJ5XyBiaWcgLSB1c2UgZ2dyYXN0ciB0byByZWR1Y2UgdGhlIHNpemUKVUsuZ2cgPC1nZ3Bsb3QoKSArIGdncmFzdHI6OnJhc3RlcmlzZShnZW9tX3BvbHlnb24oZGF0YSA9IFVLLm1hcGRhdGEsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGNvbG9yPSJncmV5MjUiLCBmaWxsPSJncmV5OTAiLCBzaXplID0gMC4wNzUpLCBkcGk9NDAwKSArIGNvb3JkX2ZpeGVkKDEpICsgdGhlbWVfbm90aGluZygpCgojcmFzdGVyaXNlKGdlb21fcG9pbnQoYWVzKGNhcmF0LCBwcmljZSwgY29sb3VyID0gY3V0KSwgZGF0YT1kaWFtb25kcyksIGRwaT0zMCkKCgoKIyBDb252ZXJ0IFVLIHJlZ2lvbnMgdG8gYmUgY29tcGF0aWJsZSB3aXRoIG1hcAojIEZpcnN0IGZpbmQgY2VudHJlIHBvaW50IGZvciBlYWNoIHJlZ2lvbgpVSy5tYXBkYXRhLnJlZ2lvbnMubWVhbmNvb3JkcyA8LSBVSy5tYXBkYXRhICU+JSBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UobWVhbi5sYXQ9bWVhbihsYXQpLCBtZWFuLmxvbmc9bWVkaWFuKGxvbmcpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpCgoKY29sbmFtZXMoVUsubWFwZGF0YS5yZWdpb25zLm1lYW5jb29yZHMpWzFdIDwtICJwaGVfY2VudHJlIgoKUEhFLnJlZ2lvbi5HUFMudWttYXAgPC0gZHBseXI6OmxlZnRfam9pbihQSEUucmVnaW9uLkdQUywgVUsubWFwZGF0YS5yZWdpb25zLm1lYW5jb29yZHMsIGJ5PSJwaGVfY2VudHJlIikKCiMgQWRkIGFydGlmaWNpYWwgbG9jYXRpb24gZm9yICdub3Qga25vd24nClBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmU9PSJOb3QgS25vd24iLCJtZWFuLmxhdCJdIDwtIDYwMDAwMApQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iTm90IEtub3duIiwibWVhbi5sb25nIl0gPC0gNTUwMDAwCgojIFNoaWZ0ICJTb3V0aCBFYXN0IiBzbGlnaHRseSB0byByZWR1Y2UgdGhlIG92ZXJsYXAgd2l0aCBMb25kb24KUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09IlNvdXRoIEVhc3QiLCJtZWFuLmxvbmciXSA8LSA0NzUwMDAKIyBTaGlmdCAiRWFzdCBvZiBFbmdsYW5kIEVhc3QiIHNsaWdodGx5IHRvIHJlZHVjZSB0aGUgb3ZlcmxhcCB3aXRoIExvbmRvbiAKUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09IkVhc3Qgb2YgRW5nbGFuZCIsIm1lYW4ubGF0Il0gPC0gMjc1MDAwCgojIE5vdCBnb2luZyB0byB0cnkgcGxvdHRpbmcgdGhlIDIgc2FtcGxlcyBmcm9tIGVsc2V3aGVyZSBpbiB0aGUgVUssIHNvIHJlbW92ZSB0aGF0IHJvdwpQSEUucmVnaW9uLkdQUy51a21hcCA8LSBQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlICE9ICJVSyAobm90IEVuZ2xhbmQpIixdCgojIENyZWF0ZSByYWRpdXMgdmFyaWFibGUgZm9yIHBsb3R0aW5nIHBpZSBzaXplcyAodXNlIGxvZzEwKG4pKjIwLDAwMCkKUEhFLnJlZ2lvbi5HUFMudWttYXAkcmFkaXVzLlVLIDwtIGxvZzEwKFBIRS5yZWdpb24uR1BTLnVrbWFwJFJlZ2lvbl9Db3VudCkqMjAwMDAKCiNQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UKClVLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cgKyBnZW9tX3NjYXR0ZXJwaWUoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcCwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGdyb3VwPXBoZV9jZW50cmUsIHI9cmFkaXVzLlVLKSwgYWxwaGE9MC44NSwgY29sb3I9TkEsIGNvbHM9YygiTmljaG9scyIsIlNTMTQiKSkgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcbkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikKClVLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIGdlb21fc2NhdHRlcnBpZV9sZWdlbmQoUEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSwicmFkaXVzLlVLIl0sIGxhYmVsbGVyPWZ1bmN0aW9uKHgpIHJvdW5kKCgxMF4oeC8yMDAwMCkpLDApLCBuPTMsIHg9MTUwMDAwLCB5PTUwMDAwMCkKClVLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIHRoZW1lX25vdGhpbmcoKQoKIz8gQWRkIGxhYmVscwpVSy5nZy5zY2F0dGVycGllLmxhYnMgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIGdlb21fbGFiZWxfcmVwZWwoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLF0sIGFlcyhtZWFuLmxvbmcsIG1lYW4ubGF0LCBsYWJlbD1waGVfY2VudHJlKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBudWRnZV94ID0gNTAwMDAsIG51ZGdlX3kgPSAtMjUwMDAsIHNlZ21lbnQuc2l6ZSAgPSAwLjEpICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKyAKICB0aGVtZS50ZXh0LnNpemUgKwogIHRoZW1lX25vdGhpbmcoKQoKVUsuZ2cuc2NhdHRlcnBpZS5sYWJzCmBgYApcClwKTm93IGRvIGFuIGVxdWl2YWxlbnQgcGxvdCBmb3Igc3VibGluZWFnZXMKYGBge3J9ClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBQSEUucmVnaW9uLkdQUy51a21hcAoKClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMSIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTFdIDwtICIxIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjIiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzEyXSA8LSAiMiIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIzIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxM10gPC0gIjMiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iNiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTRdIDwtICI2IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjgiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE1XSA8LSAiOCIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxNCIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTZdIDwtICIxNCIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxNSIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTddIDwtICIxNSIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxNiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMThdIDwtICIxNiIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSJTaW5nbGV0b24iLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzE5XSA8LSAiU2luZ2xldG9uIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW5baXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKV0gPC0gMAoKIyBNb3N0IHNhbXBsZXMgYXJlIGVpdGhlciBzdWJsaW5lYWdlIDEgb3IgMTQuIExldCdzIGNyZWF0ZSBhIGNvdW50IG9mIHNhbXBsZXMgdGhhdCBhcmUgbmVpdGhlci4KUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJGBPdGhlciBTdWJsaW5lYWdlc2AgPC0gc2FwcGx5KDE6bnJvdyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pLCBmdW5jdGlvbiAoeCkgUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJFJlZ2lvbl9Db3VudFt4XS1zdW0oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJGAxYFt4XSwgUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJGAxNGBbeF0pKSAKCgoKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnICsgZ2VvbV9zY2F0dGVycGllKGRhdGE9UEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluW1BIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiRtZWFuLmxvbmchPTAsXSwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGdyb3VwPXBoZV9jZW50cmUsIHI9cmFkaXVzLlVLKSwgYWxwaGE9MC44NSwgY29sb3I9TkEsIGNvbHM9YygiMSIsIjE0IiwiT3RoZXIgU3VibGluZWFnZXMiKSkgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcblN1YmxpbmVhZ2UiLHZhbHVlcz1jKCIjRkM5MjcyIiwiI0JDQkREQyIsICJncmV5NTAiKSwgYnJlYWtzPWMoIjEiLCIxNCIsIk90aGVyIFN1YmxpbmVhZ2VzIikpCgojIGFkZCBsZWdlbmQKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSArIGdlb21fc2NhdHRlcnBpZV9sZWdlbmQoUEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSwicmFkaXVzLlVLIl0sIGxhYmVsbGVyPWZ1bmN0aW9uKHgpIHJvdW5kKCgxMF4oeC8yMDAwMCkpLDApLCBuPTMsIHg9MTUwMDAwLCB5PTUwMDAwMCkKCiNVSy5nZy5zY2F0dGVycGllIDwtIFVLLmdnLnNjYXR0ZXJwaWUgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcApVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlICsgdGhlbWVfbm90aGluZygpCgojPyBBZGQgbGFiZWxzClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSA8LSBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgKyBnZW9tX2xhYmVsX3JlcGVsKGRhdGE9UEhFLnJlZ2lvbi5HUFMudWttYXBbIWlzLm5hKFBIRS5yZWdpb24uR1BTLnVrbWFwJG1lYW4ubGF0KSxdLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgbGFiZWw9cGhlX2NlbnRyZSksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgbnVkZ2VfeCA9IDUwMDAwLCBudWRnZV95ID0gLTI1MDAwLCBzZWdtZW50LnNpemUgID0gMC4xKSArCiAgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKyAKICB0aGVtZS50ZXh0LnNpemUgKwogIHRoZW1lX25vdGhpbmcoKQoKClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZQpgYGAKClwKQ29tYmluZWQgbWFwIHBsb3QKYGBge3J9ClVLLmdnLnNjYXR0ZXJwaWUuY29tYmkgPC0gcGxvdF9ncmlkKFVLLmdnLnNjYXR0ZXJwaWUubGFicywgVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlLCBuY29sPTIsIGxhYmVscyA9IGMoIkEiLCJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgpVSy5nZy5zY2F0dGVycGllLmNvbWJpCmBgYApcClwKUGxvdCBpbiBjb21iaW5hdGlvbiB3aXRoIGJhcnBsb3RzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwbG90X2dyaWQoVUsuZ2cuc2NhdHRlcnBpZS5jb21iaSwgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcywgbnJvdz0yLCByZWxfaGVpZ2h0cz1jKDQsNSkpCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiRmlnMl9UUEEtUEhFX01hcC1MaW5lYWdlK0JhcnBsb3RzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE5MCwgaGVpZ2h0PTE4NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCiNnZ3NhdmUocGxvdD1wbG90X2dyaWQoVUsuZ2cuc2NhdHRlcnBpZS5jb21iaSwgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcywgbnJvdz0yLCByZWxfaGVpZ2h0cz1jKDQsNSkpLCBwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzJfVFBBLVBIRV9NYXAtTGluZWFnZStCYXJwbG90cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnN2ZyIpLCB1bml0cz0nbW0nLCB3aWR0aD0xOTAsIGhlaWdodD0xODUsIGRldmljZT1zdmdsaXRlLCBkcGk9MTIwMCkKCgpgYGAKXApcCiMjIyBBbmFseXNpcyBieSBzdWJsaW5lYWdlClwKTm93IGxldHMgc3RhcnQgZXhwbG9yaW5nIGhvdyBzYW1wbGVzIGFyZSBkaXN0cmlidXRlZCBieSBzdWJsaW5lYWdlCgpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSBQSEUubWV0YWRhdGEubGlua2VkClBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1yZXYoYXMuY2hhcmFjdGVyKHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSkpKQoKUEhFLkxpbmVhZ2UuY291bnQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbD1zdW0oQ291bnQpLCBwZXJjPShDb3VudC90b3RhbCkqMTAwKQoKUEhFLnN1Ymxpbi5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsPXN1bShDb3VudCksIHBlcmM9KENvdW50L3RvdGFsKSoxMDApCgpQSEUuZ2VvLnN1Ymxpbi55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpCgoKIyMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwcwoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4KUEhFLnN1YmxpbmVhZ2UuVUtib3JuIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgdWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgI2RwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMb25kb24gYmFzZWQKUEhFLnN1YmxpbmVhZ2UuTG9uZG9uIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuQWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgTG9vayBzcGVjaWZpY2FsbHkgYXQgcGVvcGxlID4zNXlvIGFuZC9vciBiZWxvdwpQSEUuc3VibGluZWFnZS5BZ2UuMzUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Om11dGF0ZShvdmVyMzU9aWZlbHNlKGFnZV9ncm91cCAlaW4lIGMoIjM1LTQ0IiwgIjQ1KyIpLCAib3ZlcjM1IiwgaWZlbHNlKGFnZV9ncm91cCAlaW4lIGMoIjE2LTI0IiwgIjI1LTM0IiksICJ1bmRlcjM1IiwgIlVua25vd24iKSkpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgb3ZlcjM1KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhvdmVyMzUpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgSElWIGdyb3VwClBIRS5zdWJsaW5lYWdlLkhJViA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoaGl2cG9zKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYnkgUEhFIFJlZ2lvbgpQSEUuc3VibGluZWFnZS5QSEVjZW50cmUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhwaGVfY2VudHJlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgpgYGAKClwKUGxvdCBieSBzdWJsaW5lYWdlCmBgYHtyfQpwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90IDwtIGdncGxvdChQSEUuZ2VvLnN1Ymxpbi55ZWFycywgYWVzKGFzLm51bWVyaWMoeWVhciksIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBjb2xvdXI9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjY1LCBhZXMoc2l6ZT1Db3VudCkpICsgCiAgZ2VvbV9saW5lKGFscGhhPTAuMjUpICsKICBndWlkZXMoY29sb3VyPSdub25lJykgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDcsYnJlYWtzPWMoMSw1LDEwLDI1LDUwKSkgKyAKICBndWlkZXMoc2l6ZT1ndWlkZV9sZWdlbmQobnJvdz0yLCBkaXJlY3Rpb24gPSAnaG9yaXpvbnRhbCcsIGJ5cm93PVQpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJTYW1wbGUgWWVhciIsIHNpemU9IkNvdW50IikgCiNwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90CgpwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW4uY291bnQsIGFlcyhDb3VudCxUUEEucGluZWNvbmUuc3VibGluZWFnZSxmaWxsPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJzdGFjayIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcblN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iU2FtcGxlIENvdW50IikgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW4uY291bnQsIGFlcygoQ291bnQrMTIpLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgI2Nvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwyMDApKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDI2MCkpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkKI3Auc3VibGluZWFnZS5oYmFycGxvdCAKCnAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uY291bnRzLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSx4PUNvdW50LGZpbGw9Z2VuZGVyX29yaWVudGF0aW9uKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik9yaWVudGF0aW9uIix2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9Ik9yaWVudGF0aW9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTEpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90CgpwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5ISVYsIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB4PUNvdW50LGZpbGw9aGl2cG9zKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkhJViArdmUiLHZhbHVlcz1QSEUuaGl2LmNvbHMkaGl2LmNvbHMsIGJyZWFrcz1QSEUuaGl2LmNvbHMkaGl2cG9zKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IkhJViArdmUiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsgCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2UuSElWLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3Auc3VibGluZWFnZS5oaXYuaGJhcnBsb3QKCnAuc3VibGluZWFnZS51a2Jvcm4uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLlVLYm9ybiwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UseD1Db3VudCxmaWxsPXVrYm9ybikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS1xuYm9ybiIsdmFsdWVzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4uY29scywgYnJlYWtzPVBIRS51a2Jvcm4uY29scyR1a2Jvcm4pICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iVUsgYm9ybiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLlVLYm9ybiwgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnN1YmxpbmVhZ2UudWtib3JuLmhiYXJwbG90CgpwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5BZ2UsIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB4PUNvdW50ICxmaWxsPWFnZV9ncm91cCkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJBZ2Vcbkdyb3VwIix2YWx1ZXM9UEhFLkFnZS5jb2xzJGFnZV9ncm91cC5jb2xzLCBicmVha3M9UEhFLkFnZS5jb2xzJGFnZV9ncm91cCkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJBZ2UgR3JvdXAiKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5BZ2UsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdAoKCnAuc3VibGluZWFnZS5QSEVyZWdpb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLlBIRWNlbnRyZSwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQsIGZpbGw9cGhlX2NlbnRyZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIix2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFBIRS5yZWdpb24pICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iVUtIU0EgUmVnaW9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTQpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2UuUEhFY2VudHJlLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKCmBgYApcCkxvb2sgYXQgaG93IHN1YmxpbmVhZ2VzIGFyZSBkaXN0cmlidXRlZCBieSByZWdpb24gKHN1YmxpbmVhZ2UtY2VudHJpYykKYGBge3J9CnAuc3VibGluZWFnZS5QSEVyZWdpb24uaGJhcnBsb3QKYGBgCgpcCkNvbWJpbmUgcGF0aWVudCBtZXRhZGF0YSBpbnRvIGEgcGxvdApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQojUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS51a2Jvcm4uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9YygzLDIsMiwyLDIsMiwyKSwgc2NhbGU9MC45KQoKI1BIRS5zdWJsaW5lYWdlcy5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCwgcC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuUEhFcmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9YygzLDIsMiwyLDIsNCksIHNjYWxlPTAuOSkKClBIRS5zdWJsaW5lYWdlcy5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCwgcC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDQsMiwyLDIsMiksIHNjYWxlPTAuOSkKClBIRS5zdWJsaW5lYWdlcy5jb21iaXBsb3QuMSAKCmBgYAoKClwKTGV0cyBhZGQgdGhlICdhbGwnIHJvdyBhZ2FpbiB0byB0aGUgJ2J5IHN1YmxpbmVhZ2UnIHBsb3QKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEyfQojIGxlZ2VuZHMKUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubGVnZW5kcyA8LSBwbG90X2dyaWQoZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90KSwgZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsNCw0LDQsNCksIHNjYWxlPTAuOTUpCgojIHJlZ2lvbnMKI1BIRS5zdWJsaW5lYWdlLmNvbWJpcGxvdC4xLm5vbGVnZW5kIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90ICsgbGVnZW5kLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKIyBPciBkbyBpdCB2ZXJ0aWNhbGx5CnAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLnllYXIuYnViYmxlcGxvdCArIHgudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuY291bnRzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAuc3VibGluZWFnZS5oYmFyLm9yaWVudGF0aW9uLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAuc3VibGluZWFnZS5oYmFyLmhpdi5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuaGl2LmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAuc3VibGluZWFnZS5oYmFyLkFnZS5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuQWdlLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCiMgQ29tYmluZSB0aGUgcGxvdHMKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLmNvdW50cy5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIub3JpZW50YXRpb24uY29tYmksIHAuc3VibGluZWFnZS5oYmFyLmhpdi5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuQWdlLmNvbWJpLCBucm93PTEsIHJlbF93aWR0aHM9Yyg3LDMsNCw0LDQpLCBsYWJlbHM9YygiQSIsICJCIiwgIkMiLCAiRCIsICJFIiksbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgdmp1c3Q9MSwgc2NhbGU9MC45OSkKCiMgYW5kIGFkZCB0aGUgbGVnZW5kcyBvbiB0b3AKI3Auc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcyA8LSBwbG90X2dyaWQoUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubGVnZW5kcywgcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwsIG5jb2w9MSwgcmVsX2hlaWdodHM9YygxLDkpKQoKIyBsZWdlbmRzIGJlbG93CnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcyA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwsIFBIRS5zdWJsaW5lYWdlLmNvbWJpcGxvdC4xLmxlZ2VuZHMsIG5jb2w9MSwgcmVsX2hlaWdodHM9Yyg4LDEpKQoKCnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcwoKYGBgCgpcClwgClRoZXNlIHBhdHRlcm5zIGxvb2sgZmFpcmx5IHNpbWlsYXIgYmV0d2VlbiBzdWJsaW5lYWdlcywgYW5kIChhcGFydCBmcm9tIDEgJiAxNCkgdGhlIGdyb3VwcyBhcmUgdmVyeSBzbWFsbC4gSG93ZXZlciwgc3VibGluZWFnZSAxNCBkb2VzIGFwcGVhciB0byBoYXZlIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgTVNNIGNvbXBhcmVkIHRvIHN1YmxpbmVhZ2UgMSBhbmQgb3RoZXJzLiBMZXQncyB0ZXN0IHRoYXQgZm9ybWFsbHkgdXNpbmcgMngyIGZpc2hlcidzIHRlc3RzClwKYGBge3J9ClBIRS5NU00uY291bnRzLmFsbCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoaXMuTVNNLCAuZHJvcD1GKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoKGlzLk1TTSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsaXMuTVNNLCAuZHJvcD1GKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoKGlzLk1TTSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAjJT4lCiAgI2RwbHlyOjpmaWx0ZXIoIWlzLm5hKGlzLk1TTSkpCgoKUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlciA8LSBQSEUuc3VibGluZWFnZS5NU00uY291bnRzICU+JSBkcGx5cjo6c2VsZWN0KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBpcy5NU00sIENvdW50KSAlPiUKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGlzLk1TTSwgdmFsdWVzX2Zyb209Q291bnQpICU+JQogIGRwbHlyOjptdXRhdGUoTVNNPXJlcGxhY2VfbmEoTVNNLCAwKSwgT3RoZXI9cmVwbGFjZV9uYShPdGhlciwgMCksIFRvdGFsPXN1bShNU00sT3RoZXIpKSAlPiUKICAjZHBseXI6OnNlbGVjdCgtYE5BYCkgJT4lCiAgZHBseXI6OmZpbHRlcihUb3RhbCE9MCkKICAKClBIRS5zdWJsaW5lYWdlLk1TTS5wdmFsIDwtIGRhdGEuZnJhbWUoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9UEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlciRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgcC5maXNoZXI9c2FwcGx5KDE6bnJvdyhQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyKSwgZnVuY3Rpb24gKHgpIGZpc2hlci50ZXN0KG1hdHJpeChhcy5udW1lcmljKGMoUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlclt4LCJNU00iXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyW3gsIk90aGVyIl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUEhFLk1TTS5jb3VudHMuYWxsW1BIRS5NU00uY291bnRzLmFsbCRpcy5NU009PSJNU00iLCJDb3VudCJdLCBQSEUuTVNNLmNvdW50cy5hbGxbUEhFLk1TTS5jb3VudHMuYWxsJGlzLk1TTT09Ik90aGVyIiwiQ291bnQiXSkpLG5yb3c9MikpW1sxXV0pLCBzdHJpbmdzQXNGYWN0b3JzPUYpCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyIDwtIGRwbHlyOjpsZWZ0X2pvaW4oUEhFLnN1YmxpbmVhZ2UuTVNNLmNvdW50cy53aWRlciwgUEhFLnN1YmxpbmVhZ2UuTVNNLnB2YWwsIGJ5PSJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyCmBgYAoKClwKXAojIyMgVmlzdWFsaXNhdGlvbiBvZiBVSyBnZW5vbWljIHJlbGF0aW9uc2hpcHMKXApPaywgbGV0J3MgbWFrZSBhIHRyZWUgZm9yIGRpc3BsYXlpbmcgdGhlc2UgcmVsYXRpb25zaGlwcyB1c2luZyB0aGUgVUsgZGF0YXNldCBvbmx5ClwKRnJvbSBzb21lIGV4cGVyaW1lbnRhdGlvbiwgYSAnR3JhcGVUcmVlJyBtaW5pbXVtIHNwYW5uaW5nIG5ldHdvcmsgd29ya3Mgd2VsbCBmb3IgdmlzdWFsaXNpbmcgdGhlIGNsb25hbGl0eSBvZiB0aGVzZSBwb3B1bGF0aW9ucy4gV2UgY2FuIHVzZSBhIFNOUC1zY2FsZWQgcGh5bG9nZW55IGFzIGRpcmVjdCBpbnB1dCB0byBHcmFwZVRyZWUsIGFuZCB0aGlzIHdpbGwgYWxsb3cgYnJhbmNoZXMgdG8gYmUgc2NhbGVkIGFwcHJvcHJpYXRlbHkuIEhvd2V2ZXIsIGFsdGhvdWdoIGFubm90YXRpb24gaXMgYWxsb3dlZCB3aXRoaW4gdGhlIEdyYXBlVHJlZSBzb2Z0d2FyZSwgY29sb3VycyBtdXN0IGJlIG1hbnVhbGx5IGVkaXRlZC4gRmluYWwgR3JhcGVUcmVlIHBsb3RzIGNhbiB0aGVuIGJlIGltcG9ydGVkIGJhY2sgaW50byBSIGZvciBjb21iaW5pbmcgd2l0aCBvdGhlciBwbG90cy4gClwKCkFsdGVybmF0aXZlIHZpc3VhbGlzYXRpb25zIC0gZ3JhcGV0cmVlPwpcClRha2UgdGhlIDUyNi1nbG9iYWwgcGh5bG9nZW55IChzbnAtc2NhbGVkIHZlcnNpb24gZnJvbSBweWphciksIGFuZCBwcnVuZSB0byBvbmx5IGluY2x1ZGUgdGhlIFVLIHN0cmFpbnMgZnJvbSB0aGlzIHN0dWR5IC0gdGhpcyBlbnN1cmVzIHRoZSB0b3BvbG9neSBpcyBjb25zaXN0ZW50IGFjY3Jvc3Mgc3R1ZGllcy4gCmBgYHtyfQoKVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrIDwtIGFwZTo6a2VlcC50aXAoVFBBLnB5amFyLnRyZWUsIGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJEdlb19Db3VudHJ5PT0iVUsiLCJTYW1wbGVfTmFtZSJdKSkpCgoKVFBBLnB5amFyLnRyZWUuc3Vic2V0Lmdsb2JhbF9iZWFzdF9vbmx5LnNlcWxhbmVzIDwtIFRQQS5tZXRhMi4xICU+JSBmaWx0ZXIoZnVsbC50ZW1wb3JhbC5hbmFseXNpcz09J1llcycpICU+JQogIHNlbGVjdChDbGVhbmVkX2Zhc3RxX2lkKSAlPiUgcHVsbCgpCgpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuc2VxbGFuZXMgPC0gYXMuY2hhcmFjdGVyKHVubGlzdChQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkR2VvX0NvdW50cnk9PSJVSyIsIkNsZWFuZWRfZmFzdHFfaWQiXSkpCgoKZ2d0cmVlKFRQQS5weWphci50cmVlLnN1YnNldC51aykKI3dyaXRlLnRyZWUoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LnB5amFyLjIwMjItMDItMDMudHJlIikpCgojIFdyaXRlIG91dCBhIG1ldGFkYXRhIHNoZWV0IGZvciB0aGUgcmVsZXZhbnQgaW5mb3JtYXRpb24KUEhFLm1ldGFkYXRhLmxpbmtlZC5ncmFwZXRyZWUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCAieWVhciIsImdlbmRlcl9vcmllbnRhdGlvbiIsInBoZV9jZW50cmUiLCJoaXZwb3MiLCJ1a2Jvcm4iLCJUUEFfTGluZWFnZSIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIildCmNvbG5hbWVzKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlKVsxXSA8LSAiSUQiCgojd3JpdGUudGFibGUoUEhFLm1ldGFkYXRhLmxpbmtlZC5ncmFwZXRyZWUsIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLlVLLW9ubHkuZ3JhcGV0cmVlLm1ldGEuMjAyMi0wMi0wMy50c3YiKSwgc2VwID0gIlx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzID0gRikKYGBgClwKVHJlZSBpbmRlcGVuZGVudGx5IHZpc3VhbGlzZWQgYW5kIGFubm90YXRlZCB1c2luZyBHcmFwZVRyZWUuClwKTm93IGltcG9ydCBhbmQgaW50ZWdyYXRlIEdyYXBlVHJlZSBwbG90IHdpdGggbWV0YWRhdGEgcGxvdHMuCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KIyBDb21iaW5lIHRoZSBwbG90cwpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC5CMkYgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLmNvdW50cy5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIub3JpZW50YXRpb24uY29tYmksIHAuc3VibGluZWFnZS5oYmFyLmhpdi5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuQWdlLmNvbWJpLCBucm93PTEsIHJlbF93aWR0aHM9Yyg3LDQsNCw0LDQpLCBsYWJlbHM9YygiQiIsICJDIiwgIkQiLCAiRSIsICJGIiksbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgdmp1c3Q9MSwgc2NhbGU9MC45NykKCiMgbGVnZW5kcyBiZWxvdwpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC5CMkYsIFBIRS5zdWJsaW5lYWdlLmNvbWJpcGxvdC4xLmxlZ2VuZHMsIG5jb2w9MSwgcmVsX2hlaWdodHM9Yyg3LDEpKQoKI3Auc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYKCgojIE5vdyBicmluZyBpbiBleHRlcm5hbGx5IHBsb3R0ZWQgR3JhcGV0cmVlCnAuVFBBLlVLLkdyYXBldHJlZS5zdWJsaW5lYWdlcyA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoVFBBLlVLLkdyYXBldHJlZS5zdWJsaW5lYWdlcy5maWxlKQpwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMKCnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYud2l0aC5ncmFwZXRyZWUgPC0gcGxvdF9ncmlkKHAuVFBBLlVLLkdyYXBldHJlZS5zdWJsaW5lYWdlcywgcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRiwgbmNvbD0xLCBsYWJlbHM9YygiQSIsIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCByZWxfaGVpZ2h0cz1jKDMsNSkpIAoKCnAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYud2l0aC5ncmFwZXRyZWUKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJGaWcxX1RQQS1QSEVfU2FtcGxlLWRpc3Ryb3Mtc3VibGluZWFnZS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xOTAsIGhlaWdodD0xODUsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgojZ2dzYXZlKHBsb3Q9cC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRi53aXRoLmdyYXBldHJlZSwgcGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiRmlnMV9UUEEtUEhFX1NhbXBsZS1kaXN0cm9zLXN1YmxpbmVhZ2UuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5zdmciKSwgdW5pdHM9J21tJywgd2lkdGg9MTkwLCBoZWlnaHQ9MTg1LCBkZXZpY2U9c3ZnbGl0ZSwgZHBpPTEyMDApCgpgYGAKClwKTWFuYWdlIG90aGVyIEdyYXBlVHJlZSBwbG90cyAoZm9yIGNvbnNpc3RlbmN5KQoKVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMgpgYGB7cn0KIyBCcmluZyBpbiAzLXdheSBncmFwaGV0cmVlIHBsb3QgKDMgZGlmZmVyZW50IG1ldGFkYXRhIHZhcmlhYmxlcyB1c2luZyB0aGUgc2FtZSBpbnB1dCB0cmVlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnNF9UUEEtUEhFX0dyYXBldHJlZS0zd2F5cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xNDUsIGhlaWdodD0xODAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKClwKQW5kIGFsc28gZG8gdGhlIEhJViBzdGF0dXMgcGxvdApgYGB7cn0KClRQQS5VSy5HcmFwZXRyZWUuSElWIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZShUUEEuVUsuR3JhcGV0cmVlLkhJVi5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLkhJVgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWc1X1RQQS1QSEVfR3JhcGV0cmVlLUhJVi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0xMTAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKCgoKXApcCiMjIyBQaHlsb2dlbmV0aWMgY29udGV4dCBhbmFseXNlcwpcCk9rLCBub3cgbGV0cyBsb29rIGF0IHNvbWUgdHJlZXMKXApGaXJzdCwgbGV0J3MgZm9ybWFsaXNlIEJFQVNUIHRyZWUgcGxvdHRpbmcgYXMgdGhyZWUgc2VwYXJhdGUgZnVuY3Rpb25zIHRvIGVuYWJsZSBvdGhlciB0cmVlcyB0byBiZSBwbG90dGVkIHRoZSBzYW1lIHdheQpcCmBgYHtyfQpmdWxsLmJlYXN0Mi50cmVlIDwtIHJlYWQuYmVhc3QoZnVsbC5iZWFzdDIudHJlZS5maWxlKQpmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbCA8LSBnc3ViKCJcXHwuKyQiLCIiLGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8kdGlwLmxhYmVsLCBwZXJsPVQpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBmdW5jdGlvbiB0byBleHRyYWN0IGEgdHJlZSBiYXNlZCBvbiBzdWJsaW5lYWdlCkV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90IDwtIGZ1bmN0aW9uKG15LmJlYXN0LnRyZWUsIG15Lm1ldGFkYXRhLCBteS5waGUubWV0YSwgbXkuc3VibGluZWFnZSl7CiAgIyBnZXQgYWxsIHRpcHMgdG8gaW5jbHVkZSBmcm9tIG1ldGFkYXRhLCB0aGVuIGNhbGN1bGF0ZSBNUkNBIGZyb20gdHJlZQogIHN1YmxpbmVhZ2UudGVzdC5tcmNhIDwtIGdldE1SQ0EobXkuYmVhc3QudHJlZUBwaHlsbywgYXMuY2hhcmFjdGVyKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09bXkuc3VibGluZWFnZSwiU2FtcGxlX05hbWUiXSkpKQogICMjIyMjIwogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gdHJlZV9zdWJzZXQobXkuYmVhc3QudHJlZSwgbm9kZT1zdWJsaW5lYWdlLnRlc3QubXJjYSwgbGV2ZWxzX2JhY2s9MCkKICByZXR1cm4oVFBBLmJlYXN0LnN1YnRyZWUudGVzdCkKfQojRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGdW5jdGlvbiB0byBwcmVwYXJlIGEgYmVhc3QgdHJlZSB3aXRoIHRpbWVzY2FsZSBpbmRpY2F0b3JzLCBwb3N0ZXJpb3Igc3VwcG9ydCBhbmQgOTUlIEhQRCBiYXJzCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRCA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIG1yc2QuZnVsbHRyZWUpewogICMgZ2V0IE1SQ0QgZm9yIHRyZWUKICBtcnNkLkJlYXN0LnRyZWUudGVzdC5zIDwtIG1heChhcy5udW1lcmljKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRTYW1wbGVfTmFtZSAlaW4lIG15LmJlYXN0LnRyZWVAcGh5bG8kdGlwLmxhYmVsLCJTYW1wbGVfWWVhciJdKSkpCiAgbXJzZC5CZWFzdC50cmVlLnRlc3QgPC0gbHVicmlkYXRlOjp5bWQocGFzdGUwKG1yc2QuQmVhc3QudHJlZS50ZXN0LnMsIi0wNi0wMSIpKSAKICBtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUgPC0gbHVicmlkYXRlOjp5bWQobXJzZC5mdWxsdHJlZSkgCiAgI21yc2QuQmVhc3QudHJlZS50ZXN0CiAgIyBwbG90IGJhc2ljIHRyZWUKICBvcHRpb25zKGlnbm9yZS5uZWdhdGl2ZS5lZGdlPVRSVUUpCiAgcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0IDwtIGdndHJlZShteS5iZWFzdC50cmVlLCBtcnNkPW1yc2QuQmVhc3QudHJlZS50ZXN0LCBsYWRkZXJpemUgPSBULCBzaXplPTAuNCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxOTYwLDIwMjAsMTApLCBtaW5vcl9icmVha3M9c2VxKDIwMDAsIDIwMjAsIDEpKSArCiAgICB0aGVtZV90cmVlMigpICsKICAgICMgQWRkIGRhdGUgbGluZXMgZm9yIGVhc3kgaW50ZXJwcmV0YXRpb24gIAogICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciAgID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5NTAiLCBzaXplPS4yKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgICA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleTg1Iiwgc2l6ZT0uMiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpCiAgIyBBZGQgcG9zdGVyaW9yIHN1cHBvcnQgYXMgbm9kZSBwb2ludHMKICBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0ICsgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOCkpLGNvbG9yPSJncmF5NjAiLHNpemU9MixhbHBoYT0wLjUsIHNoYXBlPTE4KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOTEpKSxjb2xvcj0iZ3JheTQwIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPj0wLjk2KSksY29sb3I9ImJsYWNrIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KQogICMjIyMjIwogICMgZXh0cmFjdCA5NSUgSFBEIGludGVydmFscyAtIGdlb21fcmFuZ2Ugc2VlbXMgdW5hYmxlIHRvIGRvIGNvcnJlY3RseSB3aXRoIHRoaXMgdHJlZSAoa25vd24gYnVnIGZvciB0aXAgZGF0ZWQgdHJlZXMpLCBzbyBleHRyYWN0IGRhdGEgYW5kIHBsb3QgdXNpbmcgZ2VvbV9zZWdtZW50CiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhIDwtIGZvcnRpZnkobXkuYmVhc3QudHJlZSkKICBtaW5tYXggPC0gdChtYXRyaXgodW5saXN0KFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YVshaXMubmEoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhJGhlaWdodF8wLjk1X0hQRCksImhlaWdodF8wLjk1X0hQRCJdKSxucm93PTIpKQogIGJhcl9kZiA8LSBkYXRhLmZyYW1lKG5vZGVfaWQ9VFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhWyFpcy5uYShUUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGEkaGVpZ2h0XzAuOTVfSFBEKSwibm9kZSJdLGFzLmRhdGEuZnJhbWUobWlubWF4KSkKICBuYW1lcyhiYXJfZGYpIDwtIGMoJ25vZGVfaWQnLCdtaW4nLCdtYXgnKSAKICBiYXJfZGYgPC0gYmFyX2RmICU+JSBmaWx0ZXIobm9kZV9pZCA+IE50aXAobXkuYmVhc3QudHJlZUBwaHlsbykpCiAgYmFyX2RmIDwtIGJhcl9kZiAlPiUgbGVmdF9qb2luKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YSwgYnk9Yygnbm9kZV9pZCc9J25vZGUnKSkgIyU+JSBzZWxlY3Qobm9kZV9pZCxtaW4sbWF4LHkpCiAgI21yY2QuZGVjaW1hbCA8LSBkZWNpbWFsX2RhdGUobXJzZC5CZWFzdC50cmVlLnRlc3QpCiAgbXJjZC5kZWNpbWFsIDwtIGRlY2ltYWxfZGF0ZShtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUpCiAgCiAgIyBOb3cgYWRkIEhQRHMgdG8gcGxvdAogIHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCA8LSBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgKyBnZW9tX3NlZ21lbnQoYWVzKHg9bXJjZC5kZWNpbWFsLW1heCwgeT15LCB4ZW5kPW1yY2QuZGVjaW1hbC1taW4sIHllbmQ9eSksIGRhdGE9YmFyX2RmLCBjb2xvcj0ncmVkJywgYWxwaGE9MC4yLCBzaXplPTIuMCkKICAjIE91dHB1dCB0cmVlIAogIHJldHVybihwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QpCn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRnVuY3Rpb24gdG8gYWRkIG1ldGFkYXRhIHRvIHRyZWUKIyBIYXMgdHdvIG9wdGlvbmFsIGFyZ3VtZW50cyAiaW5pdGlhbC50cmFjay5vZmZzZXQiIGFuZCAidHJhY2suc2NhbGluZyIgd2hpY2ggY2FuIGJlIHVzZWQgdG8gYWx0ZXIgdGhlIHdpZHRoIGFuZCBwb3NpdGlvbmluZyBvZiBtZXRhZGF0YSB0cmFja3MKCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YSA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLmlucHV0LCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIGluaXRpYWwudHJhY2sub2Zmc2V0LCB0cmFjay5zY2FsaW5nKXsKICAgICMgQWRkIGNvZGUgdG8gYWxsb3cgc2NhbGluZyB1cCBvZiB0aGUgdHJhY2sgb2Zmc2V0cyBhbmQgd2lkdGhzIC0gdXNlZnVsIGZvciBtdWNoIGJpZ2dlciBsZW5ndGggdHJlZXMKICBpZihtaXNzaW5nKGluaXRpYWwudHJhY2sub2Zmc2V0KSl7CiAgICBpbml0aWFsLnRyYWNrLm9mZnNldCA8LSAwCiAgfSAgICAKICBpZihtaXNzaW5nKHRyYWNrLnNjYWxpbmcpKXsKICAgIHRyYWNrLnNjYWxpbmcgPC0gMQogIH0KICAjIENhbGN1bGF0ZSBhbW91bnQgdG8gb2Zmc2V0IGVhY2ggaGVhdG1hcCB0cmFjawogIG9mZnNldC5kaXN0IDwtIDQqdHJhY2suc2NhbGluZwogIHRyYWNrLndpZHRoIDwtICgxL21heChteS5iZWFzdC50cmVlLmlucHV0JGRhdGEkaGVpZ2h0KSozKSp0cmFjay5zY2FsaW5nCiAgCiAgIyBtYWtlIGEgbGlzdCBvZiB0YXhhIHVzZWQgaW4gdGhpcyBwbG90IAogIG15LnRheGEubGlzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KGZpbHRlcihteS5iZWFzdC50cmVlLmlucHV0JGRhdGEsIGlzVGlwPT1UUlVFKSAlPiUgc2VsZWN0KGxhYmVsKSkpCiAgCiAgIyBtYWtlIGEgY29sb3Igc2NhbGUgZm9yIHNhbXBsaW5nIHllYXJzCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9c29ydCh1bmlxdWUoYXMubnVtZXJpYyh1bmxpc3QobXkubWV0YWRhdGFbKG15Lm1ldGFkYXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0KSwiU2FtcGxlX1llYXIiXSx1c2UubmFtZXM9RikpKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDcsICJZbE9yUmQiKSkobnJvdyhQSEUuc3VibGludGVzdC55ZWFyLmNvbHMpKQogIAogICMgT3IgYWx0ZXJuYXRpdmVseSwgdXNlIGEgY29tbW9uIGNvbG91ciBzY2hlbWUgZm9yIGFsbCBkYXRhIChtYXliZSBtb3JlIHNlbnNpYmxlKQogIFBIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9VFBBLnllYXIuY3V0dG9mZi5jb2xzJGRhdGUuY3V0dG9mZiwgeWVhci5jb2xzPVRQQS55ZWFyLmN1dHRvZmYuY29scyRkYXRlLmN1dHRvZmYuY29sLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIG1ha2UgbWV0YWRhdGEgZmlsZSBmb3IgVUsgcmVnaW9ucyBwcmVzZW50IGluIHN1YmxpbmVhZ2UKICBzdWJsaW4udGVzdC5yZWdpb24ubWV0YSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJTYW1wbGVfTmFtZSJdKSksIFJlZ2lvbj1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJwaGVfY2VudHJlIl0pKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgCiAgIyBBZGQgaGVhdG1hcCBzdHJpcHMKICAjIFNhbXBsZSBZZWFyCiAgI1RQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKG15LmJlYXN0LnRyZWUuaW5wdXQsIFRQQS5yYXdzZXEuYWxsLlllYXJzLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCtvZmZzZXQuZGlzdCxjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsKICAgICNzY2FsZV9maWxsX21hbnVhbChuYW1lPSJZZWFyIiwgdmFsdWVzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMsYnJlYWtzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsIG5jb2w9MikpICsKICAgICNnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAobXkuYmVhc3QudHJlZS5pbnB1dCwgVFBBLnJhd3NlcS55ZWFyLmN1dHRvZmYucCwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCwgb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0K29mZnNldC5kaXN0LGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iWWVhciIsIHZhbHVlcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhci5jb2xzLGJyZWFrcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhciwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxLCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgCiAgIyBBZGQgY291bnRyeQogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsLCBUUEEucmF3c2VxLmNvdW50cmllcy5wLCBjb2xvcj1OVUxMLHdpZHRoPXRyYWNrLndpZHRoLCBvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrKG9mZnNldC5kaXN0KjIpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkNvdW50cnkiLCB2YWx1ZXM9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJGNvdW50cnkuY29sLCBicmVha3M9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJEdlb19Db3VudHJ5LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgIyBVSyBvciBub24tVUsKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUEEucmF3c2VxLlVLLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCozKSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJFbmdsYW5kL090aGVyIiwgYnJlYWtzPWMoIkVuZ2xhbmQiLCJPdGhlciIpLCB2YWx1ZXM9YygiYmxhY2siLCJncmV5OTUiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDMsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICAjIFVLIFBIRSByZWdpb24KICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwgc3VibGluLnRlc3QucmVnaW9uLm1ldGEsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCsob2Zmc2V0LmRpc3QqNCksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtIU0EgUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24sIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA0KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQogIAogICMgVFBBIHN1YmxpbmVhZ2UKICAjVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRiksIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCo1KSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPTIuNSkgKyAKICAjc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgCiAgCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgICBnZW9tX3Jvb3RlZGdlKDIpICsKICAgIE5VTEwKICAKICAjIGNhbGN1bGF0ZSBudW1iZXIgb2YgdGF4YQogIHRlc3QudGF4YWNvdW50IDwtIGxlbmd0aChteS50YXhhLmxpc3QpCiAgIyBBZGp1c3QgZmluYWwgcGxvdCB4IGFuZCB5IGF4aXMgdG8gbWFrZSBzcGFjZSBmb3IgbGFiZWxzIHVzaW5nIHRheGEgY291bnRzCiAgeC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih5PWMoLTAuNS0odGVzdC50YXhhY291bnQvMTUpLHRlc3QudGF4YWNvdW50KzIpLCB4PWMoeC5heGlzLmxpbWl0c1sxXSx4LmF4aXMubGltaXRzWzJdKzMpKQogIAogIHJldHVybihUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCkKfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmBgYAoKXApHcmVhdCwgbm93IGxldCdzIHBsb3QgYSBmdWxsIGJlYXN0IHRyZWUKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KIyBmdW5jdGlvbiBmb3IgeC1heGlzIHRpbWUgYnJlYWtzIG5lZWRzIHR3ZWFraW5nIGZvciB0aGUgZnVsbCB0cmVlClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQobXkuYmVhc3QudHJlZSA9IGZ1bGwuYmVhc3QyLnRyZWUsIG15Lm1ldGFkYXRhID0gVFBBLm1ldGEyLjEsIG15LnBoZS5tZXRhID0gUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXJzZC5mdWxsdHJlZSA9ICIyMDE5LTA2LTAxIikgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxNDAwLDIwMjAsNTApLCBtaW5vcl9icmVha3M9c2VxKDE5NTAsIDIwMjAsIDUpKSwgbXkubWV0YWRhdGEgPSBUUEEubWV0YTIuMSwgbXkucGhlLm1ldGEgPSBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gNSkKClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnN19UUEFfRnVsbEJlYXN0VHJlZS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0yNDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKXApOb3cgZG8gc3VibGluZWFnZSBwbG90cwpcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyBTdWJsaW5lYWdlIDEKc3VibGluZWFnZS4xLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMSksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMS4yKQoKIyBTdWJsaW5lYWdlLjIKc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMSkKCiMgU3VibGluZWFnZS44CnN1YmxpbmVhZ2UuOC50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDgpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCiMgU3VibGluZWFnZS4xNApzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCmBgYAoKXApQbG90IHRvZ2V0aGVyPwpcCk1heWJlIHdpdGggc3VibGluZWFnZSAxIGV4cGFuZGVkPwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpwLmJlYXN0LnRyZWVzLmhlYXRtYXAuc3VibGluZWFnZXMuY29tYmkub2Zmc2V0MSA8LSBwbG90X2dyaWQoc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCwgCiAgICAgICAgICBzdWJsaW5lYWdlLjgudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIG5jb2w9MiwgbGFiZWxzPWMoIkIgLSBTdWJsaW5lYWdlIDIiLCJDIC0gU3VibGluZWFnZSA4IiwiRCAtIFN1YmxpbmVhZ2UgMTQiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSwgdmp1c3Q9MS4wKQoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIgPC0gcGxvdF9ncmlkKHN1YmxpbmVhZ2UuMS50cmVlLmhlYXRtYXAsIHAuYmVhc3QudHJlZXMuaGVhdG1hcC5zdWJsaW5lYWdlcy5jb21iaS5vZmZzZXQxLCBsYWJlbHM9YygiQSAtIFN1YmxpbmVhZ2UgMSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NzUsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDYsMTEpLCB2anVzdD0yLjUpCgoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzhfVFBBLVBIRV9TdWJsaW5lYWdlLUJlYXN0VHJlZXMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjY1LCBoZWlnaHQ9MjMwLCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTmVlZCB0byBleHBsb3JlIHN1YmxpbmVhZ2UgMTQgYSBiaXQgbW9yZSB0byBnZXQgZGF0ZXMgZm9yIHRob3NlIHN1YmNsYWRlcwpgYGB7cn0Kc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGxpbmVzaXplPTAuNCkgIzMKYGBgCgpcCmBgYHtyfQojIE9rLCB0aGVyZSBhcmUgbXVsdGlwbGUgc3ViY2xhZGVzIGluIHRoaXMgdHJlZQpzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwJGRhdGEKCiMgZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLGMoIlBIRTE1MDE1MEEiLCJOTDE0IiwiVFBBX0JDQzEyMiIsIlRQQV9CQ0MxMjYiLCJQSEUxNDAwNzZBIiwiVFBBX1VLQlJHMDA4IikpICA5ODIKIyBmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbFtwaGFuZ29ybjo6RGVzY2VuZGFudHMoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgOTgyLCB0eXBlID0gYygidGlwcyIpKVtbMV1dXQoKc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QgPC0gYygiTkwxNyIsICJOTDE5IiwgIlBIRTE0MDA4NUEiLCAiUEhFMTQwMDg5QSIsICJQSEUxNTAxMThBIiwgIlBIRTE1MDEyMUEiLCAiUEhFMTUwMTMzQSIsICJQSEUxNTAxNDNBIiwgIlBIRTE1MDE0NUEiLCAiUEhFMTUwMTYyQSIsICJQSEUxNTAxNjZBIiwgIlBIRTE1MDE2OEEiLCAiUEhFMTYwMjI0QSIsICJQSEUxNjAyNDNBIiwgIlBIRTE2MDI1NUEiLCAiUEhFMTYwMjc2QSIsICJQSEUxNjAyOTBBIiwgIlBIRTE2MDMwMkEiLCAiUEhFMTYwMzA2QSIsICJQSEUxNzAzMzNBIiwgIlBIRTE3MDM0OUEiLCAiUEhFMTcwMzc0QSIsICJQSEUxNzAzODFBIiwgIlBIRTE3MDY2NEEiLCAiVFBBX0VTQkNOMDA1IiwgIlRQQV9VS0JJUjAzMiIpCgpzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCA8LSBjKCJOTDE0IiwgIlBIRTE0MDA3NkEiLCAiUEhFMTUwMTQ5QSIsICJQSEUxNTAxNTBBIiwgIlBIRTE1MDE3MEEiLCAiUEhFMTYwMTk2QSIsICJQSEUxNjAyNjNBIiwgIlBIRTE2MDI3NEEiLCAiUEhFMTYwMjg3QSIsICJQSEUxNjAyOTRBIiwgIlBIRTE2MDMxNkEiLCAiUEhFMTYwMzE3QSIsICJQSEUxNzAzNzJBIiwgIlBIRTE3MDM4NkEiLCAiUEhFMTcwMzk3QSIsICJQSEUxNzA0MDVBIiwgIlRQQV9CQ0MwODEiLCAiVFBBX0JDQzA4OCIsICJUUEFfQkNDMDg5IiwgIlRQQV9CQ0MxMDEiLCAiVFBBX0JDQzEyMiIsICJUUEFfQkNDMTI2IiwgIlRQQV9CQ0MxMzYiLCAiVFBBX0JDQzE2OSIsICJUUEFfSFVOMTgwMDA0IiwgIlRQQV9IVU4xOTAwMjAiLCAiVFBBX1VLQklSMDQ0IiwgIlRQQV9VS0JSRzAwNyIsICJUUEFfVUtCUkcwMDgiKQoKIyBHZXQgTVJDQSBkYXRlIGZvciBsb3dlciBjbGFkZQpzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdC50bXJjYSA8LSBzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhW3N1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDE0KUBwaHlsbywgc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QpLCJ4Il0KCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgMTQgbG93ZXIgY2xhZGU6ICIsc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QudG1yY2EpCgojIEdldCBNUkNBIGRhdGUgZm9yIHVwcGVyIGNsYWRlCnN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0LnRtcmNhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGFbc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAuZGF0YSRub2RlPT1nZXRNUkNBKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpQHBoeWxvLCBzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCksIngiXQoKcGFzdGUwKCJUTVJDQSBmb3Igc3VibGluZWFnZSAxNCB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdC50bXJjYSkKYGBgClwKXApFeHRyYWN0IGtleSBpbmZvcm1hdGlvbiBmb3Igc3VibGluZWFnZSA2ICh0d28gc2FtcGxlcykKYGBge3J9CnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCkKCnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YSA8LSBzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwJGRhdGEKCiMgR2V0IE1SQ0EgZGF0ZSBmb3IgdXBwZXIgY2xhZGUKc3VibGluZWFnZS42LmJlYXN0dHJlZS50bXJjYSA8LSBhcy5udW1lcmljKHN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YVtzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpQHBoeWxvLCBjKCJQSEUxMzAwNDhBIiwgIlBIRTE2MDI4M0EiKSksImJyYW5jaCJdKQoKCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgNiB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjYuYmVhc3R0cmVlLnRtcmNhKQpgYGAKCgoKXApcCiMjIyBFeHRyYWN0IHNhbXBsZSAmIHBvcHVsYXRpb24gc3RhdGlzdGljcyBmcm9tIGRhdGFzZXRzIGZvciB1c2UgaW4gbWFudXNjcmlwdCB0ZXh0ClwKRGF0YXNldCBhbmQgR2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbnMKYGBge3J9CiMgZGF0YXNldCBjb3VudHMKcGFzdGUwKCJUb3RhbCBVSyBzYW1wbGVzIGluIGNsZWFuZWQvZGVkdXBsaWNhdGVkIGRhdGFzZXQ6ICIsbnJvdyhQSEUubWV0YWRhdGEubGlua2VkKSkKcGFzdGUwKCJPZiB3aGljaDogIixucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5QSEU9PSJQSEUiLF0pLCIgZnJvbSBQSEUgUmVmIGxhYiBhdCBDb2xpbmRhbGUiKQpwYXN0ZTAoIk9mIHdoaWNoOiAiLG5yb3coUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJGlzLlBIRT09Ik90aGVyIixdKSwiIGZyb20gb3RoZXIgbGFicyIpCgojIHByb3BvcnRpb24gd2l0aCBnZW9ncmFwaGljYWwgZGF0YQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkWyhQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgJW5vdGluJSBjKCJOb3QgS25vd24iLCJVSyAobm90IEVuZ2xhbmQpIikpLF0pLCIgd2VyZSBncm91cGVkIGludG8gb25lIG9mIHRoZSA5IFBIIHJlZ2lvbnMiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09IlVLIChub3QgRW5nbGFuZCkiLF0pLCAiIHdlcmUgcmVmZXJyZWQgZnJvbSBvdXRzaWRlIEVuZ2xhbmQiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsXSksICIgaGFkIHVua25vd24gcmVnaW9uIikKCiMgY291bnRzICYgZnJhY3Rpb25zIGJ5IFBIRSByZWdpb24KUEhFLmdlby5jb3VudAoKYGBgClwKR2VuZGVyIE9yaWVudGF0aW9uIHN0YXRzCmBgYHtyfQpQSEUub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8uSElWLmNvdW50cwpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMKUEhFLnN1YmxpbmVhZ2UuQWdlCmBgYAoKXApTdWJsaW5lYWdlIERpc3RyaWJ1dGlvbnMKYGBge3J9ClBIRS5MaW5lYWdlLmNvdW50ClBIRS5zdWJsaW4uY291bnQKUEhFLmdlby5zdWJsaW5lYWdlCmBgYAoKXApNYWNyb2xpZGUgcmVzaXN0YW5jZSBzdGF0cwpgYGB7cn0KVUsubWFjcm9saWRlLnJlcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JQogIGRwbHlyOjpncm91cF9ieShBMjA1OEcsIEEyMDU5RykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudC5hbGxlbGU9bigpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKENvdW50LmFsbGVsZSksIHBlcmMuYWxsZWxlPXJvdW5kKChDb3VudC5hbGxlbGUvdG90YWwuY291bnQpKjEwMCwxKSkKVUsubWFjcm9saWRlLnJlcwoKVUsubWFjcm9saWRlLnJlcy5zdWJsaW4gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIEEyMDU4RywgQTIwNTlHKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmFsbGVsZT1uKCkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLmNvdW50PXN1bShDb3VudC5hbGxlbGUpLCBwZXJjLmFsbGVsZT1yb3VuZCgoQ291bnQuYWxsZWxlL3RvdGFsLmNvdW50KSoxMDAsMSkpClVLLm1hY3JvbGlkZS5yZXMuc3VibGluCgoKIyBDYWxjdWxhdGUgbG9uZyBmb3JtIGRmLCB3aXRoIGRpZmZlcmVudCAyM1MgYWxsZWxlcyAoQTIwNThHLCBBMjA1OUcsIFdULCBVbmNlcnRhaW4pIHYucy4gc3VibGluZWFnZQpVSy5tYWNyb2xpZGUucmVzLnN1Ymxpbi5sb25nIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgbXV0YXRlKFJlc2lzdGFuY2UuYWxsZWxlPWlmZWxzZShBMjA1OEc9PSJZZXMiLCAiQTIwNThHIiwgaWZlbHNlKEEyMDU5Rz09IlllcyIsICJBMjA1OUciLCBpZmVsc2UoKEEyMDU4Rz09Ik5vIiAmIEEyMDU5Rz09Ik5vIiksIldpbGQgVHlwZSIsICJVbmNlcnRhaW4iKSkpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIFJlc2lzdGFuY2UuYWxsZWxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcz1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMpLCAKICAgICAgICAgICAgICAgIGZyYWN0aW9uPUNvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcy90b3RhbC5zdWJsaW4pICU+JQogICNkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjphcnJhbmdlKChSZXNpc3RhbmNlLmFsbGVsZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoUmVzaXN0YW5jZS5hbGxlbGUgPSBmYWN0b3IoUmVzaXN0YW5jZS5hbGxlbGUsIGxldmVscz1yZXYoYygiQTIwNThHIiwgIkEyMDU5RyIsICJVbmNlcnRhaW4iLCAiV2lsZCBUeXBlIikpKSkKCiMgTWFrZSBwbG90IG9mIG1hY3JvbGlkZSByZXNpc3RhbmNlIGJ5IHN1YmxpbmVhZ2VzCnAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QgPC0gZ2dwbG90KFVLLm1hY3JvbGlkZS5yZXMuc3VibGluLmxvbmcsIGFlcyhDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGZpbGw9UmVzaXN0YW5jZS5hbGxlbGUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTWFjcm9saWRlXG5SZXNpc3RhbmNlXG5BbGxlbGUiLHZhbHVlcz1jKCJpbmRpYW5yZWQyIiwgInN0ZWVsYmx1ZTEiLCJncmV5NTUiLCAiZ3JleTkwIiksIGJyZWFrcz1jKCJBMjA1OEciLCAiQTIwNTlHIiwgIlVuY2VydGFpbiIsICJXaWxkIFR5cGUiKSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJQcm9wb3J0aW9uIHdpdGggTWFjcm9saWRlIFJlc2lzdGFuY2UgQWxsZWxlIikgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKSArCiAgZ2VvbV90ZXh0KGRhdGE9VUsubWFjcm9saWRlLnJlcy5zdWJsaW4ubG9uZywgYWVzKGN1bV9mcmFjdC5taWQsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQucGVyLnN1Ymxpbi5NYWNyb2xpZGVzKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90CgoKIyBDb21iaW5lIHBsb3Qgd2l0aCBzdWJsaW5lYWdlIGNvdW50IGJhcnMKcC5zdWJsaW4uTWFjcm9saWRlcy5oYmFycGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpLCBwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj1ULCBsYWJlbHM9YygiQSIsICJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90LmNvbWJpCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnOV9UUEEtUEhFX1N1Ymxpbi1NYWNyb2xpZGUtUmVzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE2MCwgaGVpZ2h0PTEyMCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYApcClwKCiMjIyBQYWlyd2lzZSBTTlAgYW5hbHlzaXMKXApPSywgd2FudCB0byBpbnZlc3RpZ2F0ZSB0aGUgZGlmZmVyZW50IHBhdHRlcm5zIG9ic2VydmFibGUgZm9yIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQgKHBhbGUgYmx1ZSkgaW4gU3VibGluZWFnZSAxClwKTXVsdGlwbGUgd2F5cyB3ZSBjYW4gZG8gdGhpcyAtIGluY2x1ZGluZyBTTlAgZGlzdGFuY2VzIChhbHNvIG11bHRpcGxlIHdheXMgdG8gZG8gdGhhdCkKXApgYGB7cn0KIyMjCiNVc2UgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGZyb20gdGhlIFNOUCBzY2FsZWQgdHJlZQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdCA8LSBhcGU6OmNvcGhlbmV0aWMucGh5bG8oVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrKQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0IDwtIGRhdGEuZnJhbWUoVGF4YTE9cm93Lm5hbWVzKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0KSwgVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAlPiUgdGlkeXI6OmdhdGhlcihUYXhhMiwgRGlzdGFuY2UuUGh5bG8sIC1UYXhhMSkKIyBUYXhhIENvbXBhcmlzb25zIGxhYmVsClRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YV9jb21iaW5hdGlvbiA8LSBzYXBwbHkoMTpucm93KFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQpLCBmdW5jdGlvbiAoeCkgcGFzdGUwKHNvcnQoYyhhcy5jaGFyYWN0ZXIoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCRUYXhhMVt4XSksYXMuY2hhcmFjdGVyKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YTJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQojIE1lcmdlIHRvZ2V0aGVyCiNUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSBkcGx5cjo6bGVmdF9qb2luKFRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0LCBUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0WyxjKCJUYXhhX2NvbWJpbmF0aW9uIiwiRGlzdGFuY2UuUGh5bG8iKV0sIGJ5PSJUYXhhX2NvbWJpbmF0aW9uIikKClRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0IDwtIFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQKCgpUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSB1bmlxdWUoVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQpCmBgYApcCk9rLCBub3cgYnJpbmcgaW4gc29tZSBtZXRhZGF0YSBhbmQgY29tcGFyaXNvbnMKYGBge3J9CiMgQnJpbmcgaW4gYW5kIG1lcmdlIG1ldGFkYXRhClBIRS5tZXRhLnBhaXJ3aXNlLnQxIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwieWVhciIsInBoZV9jZW50cmUiLCJsb25kb24iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsICJUUEFfTGluZWFnZSIsIkdlb19Db3VudHJ5IiwiaXMuVUsiLCJpcy5QSEUiLCAiU2FtcGxlX1llYXIiLCJkYXRlLmRlY2ltYWwiKV0KCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQxKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDEpLCIudDEiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MSlbMV0gPC0gIlRheGExIgpQSEUubWV0YS5wYWlyd2lzZS50MiA8LSBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInllYXIiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCAiVFBBX0xpbmVhZ2UiLCJHZW9fQ291bnRyeSIsImlzLlVLIiwiaXMuUEhFIiwgIlNhbXBsZV9ZZWFyIiwiZGF0ZS5kZWNpbWFsIildCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQyKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDIpLCIudDIiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MilbMV0gPC0gIlRheGEyIgoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQsUEhFLm1ldGEucGFpcndpc2UudDEsIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKSAKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLFBIRS5tZXRhLnBhaXJ3aXNlLnQyLCBieT0iVGF4YTIiLCB0eXBlPSJsZWZ0IikKCiMgRXhjbHVkZSBtaXNzaW5nIGRhdGEgKGUuZy4gbWlzc2luZyBzdWJsaW5lYWdlKSAtIHRoaXMgd2lsbCBhbHNvIHJlbW92ZSBub24tVUsgc2FtcGxlcywgc2luY2UgZnVsbCBtZXRhZGF0YSBpcyBtaXNzaW5nIGhlcmUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQxKSxdClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MiksXQoKYGBgCgpcCkRlZmluZSBjb21wYXJpc29ucwpgYGB7cn0KIyBTYW1lIHNhbXBsZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGUgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUYXhhMT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGEyLCJzYW1lIiwgImRpZmZlcmVudCIpCgojIFllYXJzIGJldHdlZW4gc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci50MSkgLSBhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSR5ZWFyLnQyKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRTYW1wbGVfWWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDIpKQoKIyBZZWFycyBiZXR3ZWVuIGRlY2ltYWwgZGF0ZSAobW9yZSBwcmVjaXNlIHRlbXBvcmFsIGRpc3RhbmNlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlIDwtIGFicyhhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkYXRlLmRlY2ltYWwudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGF0ZS5kZWNpbWFsLnQyKSkKCiMgRXBpZGVtaW9sb2dpY2FsIHRpbWUgYmV0d2VlbiAtIGNhdGFnb3JpY2FsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8MS8xMiwibW9udGgiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9My8xMiwgInF1YXJ0ZXIiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9Ni8xMiwgImhhbGYgeWVhciIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xLCAiMSB5ZWFyIixpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MiwgIjIgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MywgIjMgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NCwgIjQgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NSwgIjUgeWVhcnMiLCAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTYsICI2IHllYXJzIiwiPjYgeWVhcnMiKSkpKSkpKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0IDwtIGZhY3RvcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBsZXZlbHM9YygibW9udGgiLCAicXVhcnRlciIsImhhbGYgeWVhciIsIjEgeWVhciIsICIyIHllYXJzIiwgIjMgeWVhcnMiLCAiNCB5ZWFycyIsICI1IHllYXJzIiwgIjYgeWVhcnMiLCAiPjYgeWVhcnMiKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQueWVhcnMgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTEsICIwIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTIsICIxIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTMsICIyIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTQsICIzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTUsICI0IiwgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD02LCAiNSIsIj41IikpKSkpKQoKCiMgU2FtZSBjb3VudHJ5ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLmNvdW50cnkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MSA9PSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkR2VvX0NvdW50cnkudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIElzIFVLClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuVUsudDEgPT0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGlzLlVLLnQyLCAic2FtZSIsICJkaWZmZXJlbnQiKQoKIyBJcyBQSEUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGguUEhFIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuUEhFLnQxID09IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpcy5QSEUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIFNhbWUgVFBBIExpbmVhZ2UgKGNsZWFuZWQgdXAgY2xhc3NpZmljYXRpb25zKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQV9MaW5lYWdlLnQxPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDFbeF09PSIwIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEFfTGluZWFnZS50Mlt4XT09IjAiKSxOQSxQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZVt4XSkpCgojIFNhbWUgVFBBIHN1YmxpbmVhZ2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXIgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQyLCJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKCgoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MVt4XT09IlNpbmdsZXRvbiIpIHwoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50Mlt4XT09IlNpbmdsZXRvbiIpKSwiZGlmZmVyZW50IixQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuUGluZWNvbmUuY2x1c3Rlclt4XSkpCgojIERlZmluZSBHZW5ldGljIHJlbGF0aW9uc2hpcHMgaGllcmFyY2hpY2FsbHkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbm9taWMuY2x1c3Rlci5oaWVyYXJjaHkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSREaXN0YW5jZT09MCwiWmVyb19TTlBzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyPT0ic2FtZSIsIlNhbWUgU3VibGluZWFnZSIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZT09InNhbWUiLCAiU2FtZSBMaW5lYWdlIiwiRGlmZmVyZW50IExpbmVhZ2UiKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2Vub21pYy5jbHVzdGVyLmhpZXJhcmNoeS5waCA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLCJaZXJvX1NOUHMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXI9PSJzYW1lIiwiU2FtZSBTdWJsaW5lYWdlIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlPT0ic2FtZSIsICJTYW1lIExpbmVhZ2UiLCJEaWZmZXJlbnQgTGluZWFnZSIpKSkKCgojIFNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5QSEUucmVnaW9uIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUuY2VudHJlLmNvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxW3hdKSxhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQoKIyBkb2VzIHRoZSBjb21iaW5hdGlvbiBpbmNsdWRlZCBMb25kb24/ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpbnZvbHZlcy5Mb25kb24gPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxPT0iTG9uZG9uIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQyPT0iTG9uZG9uIiwgIkxvbmRvbiIsICJub3QtTG9uZG9uIikKCgojIE9yaWVudGF0aW9uIHBhaXIKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJE9yaWVudGF0aW9uX2NvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0pLGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdKSkpLGNvbGxhcHNlPSJfX18iKSkKCiNQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkT3JpZW50YXRpb24uQ2xhc3MgPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbiAoeCkgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TTSIsICJNU00iLAojICAgICAgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQxW3hdPT0iTVNNIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJNU00iLCAiTWl4ZWQiLCAKIyAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU1ciICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09IldTTSIsIkhldGVyb3NleHVhbCIsIAojICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJXU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TVyIsIkhldGVyb3NleHVhbCIsIlVua25vd24iKSkpKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRPcmllbnRhdGlvbi5DbGFzcyA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSksIGZ1bmN0aW9uICh4KSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IkdCTVNNIiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJHQk1TTSIsICJHQk1TTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF0gJWluJSBjKCJNU1ciLCJXU00iKSwiSGV0ZXJvc2V4dWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdICVpbiUgYygiTVNXIiwiV1NNIiksICJNaXhlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIsICJNaXhlZCIsICJVbmtub3duIikpKSkpCiAgICAgICAgICAgICAgICAgICAgCgoKIyBDb3VudHJ5IENvbXBhcmlzb25zIGxhYmVsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRDb3VudHJ5X2NvbWJpbmF0aW9ucyA8LSBwYXN0ZTAoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJEdlb19Db3VudHJ5LnQxLCJfX18iLFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MikKCiMgU3Vic2V0IHRvIFBIRSBkYXRhIG9ubHkgKGVmZmVjdGl2ZWx5IGFscmVhZHkgZG9uZSwgYnV0IGxldCdzIGJlIGV4cGxpY2l0KQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVsoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGgudWs9PSJzYW1lIiAmICBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkYm90aC5QSEU9PSJzYW1lIiksXQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUub25seT09IlBIRSIsXQoKUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrPT0ic2FtZSIpLF0KCmBgYApcClwKCmBgYHtyfQojIE1ha2Ugc2luZ2xlIHNpZGVkClBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshZHVwbGljYXRlZChQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGFfY29tYmluYXRpb24pLF0KCmBgYAoKClwKXAojIyMgUGVyZm9ybSBhIG1vcmUgZGV0YWlsZWQgYW5hbHlzaXMgb2Ygc2FtcGxlcyBmcm9tIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQKXApEbyBhIG1vcmUgZGV0YWlsZWQgZXhwbG9yYXRpb24gb2YgdGhlIE5vcnRoIEVhc3Qgb2YgRW5nbGFuZApcCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00fQpQSEUubWV0YWRhdGEubGlua2VkMi5yZWdpb25fTm9ydGhFYXN0IDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlPT0iTm9ydGggRWFzdCIsXQoKIyBDb25zdHJhaW4gYnkgc2FtcGxlcyBiZWluZyBmcm9tIHRoZSBOb3J0aCBFYXN0ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09Ik5vcnRoIEVhc3QiICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiksXQoKIyBDb25zdHJhaW4gYnkgdGhlIHNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnNbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyRzYW1lLlBIRS5yZWdpb249PSJzYW1lIixdCgojSnVzdCBwbG90IHRoZXNlIGRpc3Ryb3MKcC5Ob3J0aEVhc3QuUGFpcndpc2UuU05Qcy51bmNvbnN0cmFpbmVkIDwtIGdncGxvdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLCBhZXMoRGlzdGFuY2UuUGh5bG8pKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgbGFicyh4PSJQYWlyd2lzZSBTTlAgRGlzdGFuY2UiLCB5PSJDb21wYXJpc29uIENvdW50IikKCnAuTm9ydGhFYXN0LlBhaXJ3aXNlLlNOUHMudW5jb25zdHJhaW5lZApgYGAKClwKTWFrZSBhIHNpbmdsZSBsaW5rYWdlIG5ldHdvcmsgZnJvbSB0aGUgTm9ydGggRWFzdCBzYW1wbGVzCmBgYHtyfQoKIyBDb25zdHJhaW4gYnkgU05QIGRpc3RhbmNlIChsb29zZXIgdGhhbiBwcmV2aW91c2x5IC0gd2UganVzdCB3YW50IHRvIGZpbmQgYmFzaWMgZ3JvdXBpbmdzIHdpdGhpbiBzdWJsaW5lYWdlIDEgZm9yIE5FIHNhbXBsZXMpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvPD0yLF0KCiMgQW5kIG1ha2Ugc3VyZSB0aGF0IHdlIGFjdHVhbGx5IGhhdmUgZ2VuZXRpYyBkaXN0YW5jZSBkYXRhIGZvciBhbGwgc2FtcGxlcyB3aXRoaW4gdGhlIG5ldHdvcmsKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvKSxdCgojIGNsZWFudXAgc29tZSBkYXRhIG5vaXNlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyR5ZWFyLnQxKSxdCgojIHByZXBhcmUgaW50cHV0IGRhdGEgKHdpdGggZWRnZSBpbmZvKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyxjKCJUYXhhMSIsIlRheGEyIiwiRGlzdGFuY2UuUGh5bG8iLCJkZWNpbWFsLmRhdGUuZGlzdGFuY2UiLCJ5ZWFyLmRpc3RhbmNlIiwiT3JpZW50YXRpb24uQ2xhc3MiLCJlcGkudGltZS5kaXN0YW5jZS5jYXQiKV0KCiMjIyMjIyMjIyMjIwojIHNvbWUgaXNzdWVzIHdpdGggdXBkYXRlIHRvIFI0IC0gZG91YmxlIHNpZGVkIG1hdHJpeApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVt4LGMoIlRheGExIiwiVGF4YTIiKV0pKSksY29sbGFwc2U9Il9fXyIpKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSksXQoKIyBBbHNvIGhhdmluZyBhbiBpc3N1ZSB3aXRoIHRheGEgYXMgZmFjdG9ycyBoZXJlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGExIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEkVGF4YTIgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGEyKQojIyMjIyMjIyMjIyMKCiNpbnZlcnNlIHdlaWdodApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5pbnYgPC0gMS9QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5QaHlsbwoKIyBNYWtlIGFjdHVhbCBuZXR3b3JrCnNldC5zZWVkKDEyMzUpClBIRS5Ob3J0aEVhc3QubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gRikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLk5vcnRoRWFzdC5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gIkRpc3RhbmNlLmludiIpClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyRUYXhhMSA8LSBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuZ2ckdmVydGV4Lm5hbWVzCgojIGV4dHJhY3QgdGVtcG9yYWwgY2x1c3RlcnMgZnJvbSBuZXR3b3JrClBIRS5Ob3J0aEVhc3QubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuTm9ydGhFYXN0Lm5ldHdvcmspClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIGRhdGEuZnJhbWUoVGF4YTE9bmV0d29yay52ZXJ0ZXgubmFtZXMoUEhFLk5vcnRoRWFzdC5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5Ob3J0aEVhc3QubmV0d29yay5pZykpLCBjbHVzdGVyPWlncmFwaDo6Y29tcG9uZW50cyhQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuaWcpJG1lbWJlcnNoaXApCiMgRm9yIGVhc2Ugb2Ygc3RvcnkgdGVsbGluZyBpbiB0aGUgcGFwZXIsIGZsaXAgY2x1c3RlcnMgMiBhbmQgMyBhcm91bmQgKHNvIHdlIGNhbiB0YWxrIGFib3V0IDIgZmlyc3QpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlci5vbGQ9Y2x1c3RlciwgY2x1c3Rlcj1pZmVsc2UoY2x1c3Rlci5vbGQ9PTIsIDMsIGlmZWxzZShjbHVzdGVyLm9sZD09MywyLGNsdXN0ZXIub2xkKSkpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRjbHVzdGVyKQoKIyBtZXJnZSBtZXRhZGF0YSBiYWNrIGluClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgYnk9IlRheGExIiwgdHlwZT0ibGVmdCIpCgpgYGAKXApQbG90IG5ldHdvcmsKYGBge3J9CiMgUGxvdCBuZXR3b3JrCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAgPC0gZ2dwbG90KFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSkgKyAKICBnZW9tX2VkZ2VzKGFscGhhPTAuOTAsIGN1cnZhdHVyZSA9IDAuMiwgYWVzKGNvbG9yPWZhY3RvcihEaXN0YW5jZS5QaHlsbyksIGxpbmV0eXBlPWZhY3RvcihEaXN0YW5jZS5QaHlsbykpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmV5NSIsImdyZXk1NSIsImdyZXk4NSIpLCBuYW1lPSJTTlBcbkRpc3RhbmNlIikgKwogIHNjYWxlX2xpbmV0eXBlKG5hbWU9IlNOUFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgZ2VvbV9ub2RlbGFiZWwoYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiwgbGFiZWw9cGFzdGUoVGF4YTEseWVhcixzZXA9IlxuIiksZm9udGZhY2UgPSAiYm9sZCIpLCBhbHBoYT0wLjgsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbi0wLjQsIGxhYmVsLnNpemU9MC4xNSwgbGFiZWwucGFkZGluZyA9IHVuaXQoMC4wNSwgImxpbmVzIikpICsKICBnZW9tX25vZGVzKHNpemU9MS4wLCBhZXMoY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAKCmBgYAoKClwKT2ssIHNvIHRocmVlIG5ldHdvcmtzLiBDbGVhciBkaWZmZXJlbnRpYXRpb24gb2YgYSBoZXRlcm9zZXh1YWwgbmV0d29yayAod2l0aCAwLXNucCBkaXN0YW5jZXMpIGFuZCB0d28gcHJlZG9taW5hbnRseSBNU00gbmV0d29ya3MKXApMZXQncyBsb29rIGF0IHRoZSBwaHlsb2dlbmV0aWMgY29udGV4dCBvZiB0aG9zZSBOb3J0aCBFYXN0IGNsdXN0ZXJzIHdlJ3ZlIGRlZmluZWQuClB1bGwgb3V0IHN1YnRyZWVzIChmcm9tIHN1YmxpbmVhZ2UgMSBzdWJ0cmVlKQpcCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiMgQ2x1c3RlciAxCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEgPC0gZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50c1tQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyPT0iQ2x1c3RlcjEiLCJUYXhhMSJdKQpCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gdHJlZV9zdWJzZXQoZnVsbC5iZWFzdDIudHJlZSwgbm9kZT1CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLCBsZXZlbHNfYmFjaz0wKQoKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBpbml0aWFsLnRyYWNrLm9mZnNldCA9IDEwKQoKIyBDYW4ndCBmaXQgaW4gdGlwIGxhYnMsIGJ1dCBzaW5jZSB0aGlzIGlzIGEgcG9seXBoeWxldGljIHN1YnRyZWUsIGl0IHdvdWxkIGJlIGhlbHBmdWwgdG8gYWRkIGEgdHJhY2sgdG8gaGlnaGxpZ2h0IHRoZSBORSBzdHJhaW5zClBIRS5tZXRhZGF0YS5saW5rZWQkaXMuTm9ydGhFYXN0IDwtIGlmZWxzZShQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiTm9ydGggRWFzdCIsICJPdGhlciBFbmdsYW5kIikKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUsIGBOb3J0aCBFYXN0YD1QSEUubWV0YWRhdGEubGlua2VkJGlzLk5vcnRoRWFzdCksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTEwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkVuZ2xhbmQiLCB2YWx1ZXM9YygiI0E2Q0VFMyIsImdyZXk5NSIpLCBicmVha3M9YygiTm9ydGggRWFzdCIsIk90aGVyIEVuZ2xhbmQiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIEp1c3QgY29uZmlybSB0aGUgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIChtYWtlIHN1cmUgaXQgZG9lc24ndCBlbmNsb3NlIG90aGVyIGNsdXN0ZXJzKQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MTArKDQqNiksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuQ2x1c3RlciIsIHZhbHVlcz1jKCIjN2ZjOTdmIiwiI2JlYWVkNCIsIiNmZGMwODYiKSwgYnJlYWtzPWMoIkNsdXN0ZXIxIiwiQ2x1c3RlcjIiLCJDbHVzdGVyMyIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHNbMl0rNCksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkbGFiZWwpKS8xNSksbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkrMikpICsKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDbHVzdGVyIDIKQmVhc3QudHJlZS5ORS5jbHVzdGVyMiA8LSBnZXRNUkNBKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzW1BIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXI9PSJDbHVzdGVyMiIsIlRheGExIl0pCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSB0cmVlX3N1YnNldChmdWxsLmJlYXN0Mi50cmVlLCBub2RlPUJlYXN0LnRyZWUuTkUuY2x1c3RlcjIsIGxldmVsc19iYWNrPTEpCgpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIGluaXRpYWwudHJhY2sub2Zmc2V0ID0gMjApICsgZ2VvbV90aXBsYWIoc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBhbGlnbj1ULCBvZmZzZXQ9NSwgbGluZXNpemU9MC40KQojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTIwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKIyBhZGQgYSBiaXQgbW9yZSByb29tIHRvIHRoZSB4IGF4aXMKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIDwtIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeD1jKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMV0scC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0c1syXSsxMiksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKS8yMCktMSxsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKSswLjUpKSArIAogIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuNSwwLDAsMCwgdW5pdD0ibW0iKSkKCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENsdXN0ZXIgMwpCZWFzdC50cmVlLk5FLmNsdXN0ZXIzIDwtIGdldE1SQ0EoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHNbUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3Rlcj09IkNsdXN0ZXIzIiwiVGF4YTEiXSkKQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHRyZWVfc3Vic2V0KGZ1bGwuYmVhc3QyLnRyZWUsIG5vZGU9QmVhc3QudHJlZS5ORS5jbHVzdGVyMywgbGV2ZWxzX2JhY2s9MSkKCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgaW5pdGlhbC50cmFjay5vZmZzZXQgPSAyNikgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGFsaWduPVQsIG9mZnNldD0zLCBsaW5lc2l6ZT0wLjQpCgojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTI2Kyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZS54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMl0rMTIpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkvMjApLTEsbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkrMC41KSkgKyAKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZQoKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklECiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSAKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIApgYGAKClwKU2luY2UgQ2x1c3RlciAxIGlzIHJlYWxseSBxdWl0ZSBwb2x5cGh5bGV0aWMsIGl0IG1heWJlIG1vcmUgdXNlZnVsIHRvIHNob3cgdGhlIGNsdXN0ZXJzIGluIGNvbnRleHQgZm9yIHRoYXQgb25lCgpgYGB7cn0KIyBBZGQgTm9ydGggRWFzdCBpZGVudGlmaWVyIGNvbHVtbgpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBnaGVhdG1hcChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwLCBkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBgTm9ydGggRWFzdGA9UEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5Ob3J0aEVhc3QpLCBjb2xvcj1OVUxMLHdpZHRoPSgxL21heChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwJGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjUpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5FbmdsYW5kIiwgdmFsdWVzPWMoIiNBNkNFRTMiLCJncmV5OTUiKSwgYnJlYWtzPWMoIk5vcnRoIEVhc3QiLCJPdGhlciBFbmdsYW5kIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQoKIyBKdXN0IGNvbmZpcm0gdGhlIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSAobWFrZSBzdXJlIGl0IGRvZXNuJ3QgZW5jbG9zZSBvdGhlciBjbHVzdGVycykKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjYpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5DbHVzdGVyIiwgdmFsdWVzPWMoIiM3ZmM5N2YiLCIjYmVhZWQ0IiwiI2ZkYzA4NiIpLCBicmVha3M9YygiQ2x1c3RlcjEiLCJDbHVzdGVyMiIsIkNsdXN0ZXIzIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA2LCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIGFkZCBhIGJpdCBtb3JlIHJvb20gdG8gdGhlIHggYXhpcwpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzIDwtIGdncGxvdF9idWlsZChwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCkkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kcmFuZ2UkcmFuZ2UKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih4PWMocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQueC5heGlzLmxpbWl0c1sxXSxwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzJdKzQpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkvMTUpLGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQkZGF0YSRsYWJlbCkpKzIpKQoKIyByZWR1Y2Ugc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZCBzY2FsZXMKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjk1LDAsMCwwLCB1bml0PSJtbSIpKQpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodAoKYGBgCgoKXCAKUGxvdCB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMSA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBuY29sPTEsIGxhYmVscz1jKCJDIC0gQ2x1c3RlciAyIiwgIkQgLSBDbHVzdGVyIDMiKSwgdmp1c3Q9MS4wLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1KQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMiA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQud2l0aF9jbHVzdGVySUQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDMsMiksIGxhYmVscz1jKCJCIC0gQ2x1c3RlciAxIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkyCgoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMyA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDgsNyksIGxhYmVscz1jKCJCIC0gU3VibGluZWFnZSAxIChBbGwpIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1LCB2anVzdD0xLjApCgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkzCgpgYGAKClwKXApMb29rIG1vcmUgY2xvc2VseSBhdCBwb3B1bGF0aW9uIGRlbW9ncmFwaGljcyBvZiB0aGVzZSBjbHVzdGVycwpgYGB7cn0KIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDIKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDMKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBDb3VudHJ5IGluZm8gb24gTkUgY2x1c3RlciAzClRQQS5tZXRhMi4xICU+JSAKICBkcGx5cjo6ZmlsdGVyKFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlb19Db3VudHJ5KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkKCiMgU2VwYXJhdGUgbWV0YWRhdGEgcmVjb3JkcyBzaG93IEh1bmdhcmlhbiBzYW1wbGUgIlRQQV9IVU4xODAwMDEiIGNhbWUgZnJvbSBhIG1hbGUgYmlzZXh1YWwgKE1TV00pLgpgYGAKXApFeGFtaW5lIFNOUCBzY2FsZWQgdHJlZSBmb3IgZGlzdGFuY2VzCmBgYHtyfQoKIyBFeHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IFNOUCBkaXN0YW5jZXMKVFBBLk5FY2x1c3RlcjMucHlqYXJ0cmVlLm1yY2EgPC0gZ2V0TVJDQShUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChUUEEubWV0YTIuMVtUUEEubWV0YTIuMSRTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwsIlNhbXBsZV9OYW1lIl0pKSkKCgpUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSA8LSB0cmVlX3N1YnNldChUUEEucHlqYXIudHJlZSwgbm9kZT1UUEEuTkVjbHVzdGVyMy5weWphcnRyZWUubXJjYSwgbGV2ZWxzX2JhY2s9MSkKCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkkZGF0YQpgYGAKClwKXApEbyBzb21lIGFuYWx5c2lzIG9mIG5lYXJlc3QgbmVpZ2hib3VyIGFuZCBkaXN0YW5jZXMgdG8gTVJDQXMKYGBge3J9CmNhbGN1bGF0ZS55ZWFycy5mcm9tLm1yY2EgPC0gZnVuY3Rpb24oY3VycmVudC5nZ3RyZWUucGh5bG8sIGN1cnJlbnQuZ2d0cmVlLmRhdGEpewogICNjdXJyZW50LmdndHJlZSA8LSBCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUKICBhbGwudGlwcyA8LSBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwKICBkaXN0LjIubXJjYSA8LSBOVUxMCiAgIyMjIHB1dCBkYXRlcyBpbnRvIGRmCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLm1lZGlhbiA8LSAyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4KICBjdXJyZW50LmdndHJlZS5kYXRhJHllYXIgPC0gYXMubnVtZXJpYyhyb3VuZCgyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4sMykpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1aGlnaCA8LSByb3VuZCgyMDE5LjUgLSBzYXBwbHkoMTpucm93KGN1cnJlbnQuZ2d0cmVlLmRhdGEpLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWModW5saXN0KGN1cnJlbnQuZ2d0cmVlLmRhdGFbeCwiaGVpZ2h0XzAuOTVfSFBEIl0pKVsxXSksIDMpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1bG93IDwtIHJvdW5kKDIwMTkuNSAtIHNhcHBseSgxOm5yb3coY3VycmVudC5nZ3RyZWUuZGF0YSksZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh1bmxpc3QoY3VycmVudC5nZ3RyZWUuZGF0YVt4LCJoZWlnaHRfMC45NV9IUEQiXSkpWzJdKSwgMykKICAjIGV4dHJhY3QgZGF0ZXMgYmV0d2VlbiBzYW1wbGUgYW5kIGl0cyBNUkNBIHVzaW5nIGxvb3AKICBmb3IgKGN1cnJlbnQubm9kZSBpbiBhbGwudGlwcykgewogICAgY3VycmVudC5wYXJlbnQgPC0gYyhtYXRjaChjdXJyZW50Lm5vZGUsY3VycmVudC5nZ3RyZWUucGh5bG8kdGlwLmxhYmVsKSwgcGhhbmdvcm46OkFuY2VzdG9ycyhjdXJyZW50LmdndHJlZS5waHlsbywgbWF0Y2goYyhjdXJyZW50Lm5vZGUpLCBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwpLCAicGFyZW50IikpCiAgICAKICAgIGN1cnJlbnQubm9kZWxpc3QgPC0gY3VycmVudC5nZ3RyZWUuZGF0YVtjdXJyZW50LmdndHJlZS5kYXRhJG5vZGUgJWluJSBjdXJyZW50LnBhcmVudCxdCiAgICBjdXJyZW50LmRpc3QuMi5tcmNhIDwtIGMoY3VycmVudC5ub2RlLCBhcy5udW1lcmljKGN1cnJlbnQubm9kZWxpc3RbMSwieWVhciJdLWN1cnJlbnQubm9kZWxpc3RbMiwieWVhciJdKSkKICAgIGRpc3QuMi5tcmNhIDwtIHJiaW5kKGRpc3QuMi5tcmNhLCBjdXJyZW50LmRpc3QuMi5tcmNhKQogIH0KICBkaXN0LjIubXJjYSA8LSBkYXRhLmZyYW1lKFNhbXBsZV9OYW1lPWFzLmNoYXJhY3RlcihkaXN0LjIubXJjYVssMV0pLCBkaXN0LnRvLm1yY2E9YXMubnVtZXJpYyhkaXN0LjIubXJjYVssMl0pLCBzdHJpbmdzQXNGYWN0b3JzPUYpCiAgcmV0dXJuKGRpc3QuMi5tcmNhKQp9CgojIyMgQWxsIHNhbXBsZXMgaW4gZ2xvYmFsIHRyZWUKZGlzdC5tcmNhLmFsbC5UUEEgPC0gY2FsY3VsYXRlLnllYXJzLmZyb20ubXJjYShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBmdWxsLmJlYXN0Mi50cmVlQGRhdGEpCgpgYGAKXApNZXJnZSBkaXN0Mk1SQ0Egd2l0aCBtZXRhZGF0YQpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EgPC0gbGVmdF9qb2luKFBIRS5tZXRhZGF0YS5saW5rZWQsIGRpc3QubXJjYS5hbGwuVFBBLCBieT0iU2FtcGxlX05hbWUiKQoKcC50aW1lMm1yY2Eub3JpZW50YXRpb24gPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoZ2VuZGVyX29yaWVudGF0aW9uLCBkaXN0LnRvLm1yY2EsIGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbikpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJHZW5kZXIgT3JpZW50YXRpb24iLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IkdlbmRlciBPcmllbnRhdGlvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKQoKcC50aW1lMm1yY2EucGhlX3JlZ2lvbiA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhwaGVfY2VudHJlLCBkaXN0LnRvLm1yY2EsIGNvbG9yPXBoZV9jZW50cmUpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKHlsaW09YygwLDQwKSkgKwogIGxhYnMoeD0iVUtIU0EgUmVnaW9uIiwgeT0iWWVhcnMgdG8gTVJDQSIsIGNvbG9yPSJVS0hTQSBSZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pCgpwLnRpbWUybXJjYS5waGVfcmVnaW9uLm9yaWVudGF0aW9uIDwtIGdncGxvdChQSEUubWV0YWRhdGEubGlua2VkLmRpc3QybXJjYSwgYWVzKHBoZV9jZW50cmUsIGRpc3QudG8ubXJjYSwgY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBnZW9tX3F1YXNpcmFuZG9tKHNpemU9MC43NSwgYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArIHRoZW1lLnRleHQuc2l6ZSArCiAgY29vcmRfZmxpcCh5bGltPWMoMCwyMCkpICsKICBsYWJzKHg9IlVLSFNBIFJlZ2lvbiIsIHk9IlllYXJzIHRvIE1SQ0EiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJHZW5kZXJcbk9yaWVudGF0aW9uIiwgdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikKcC50aW1lMm1yY2EucGhlX3JlZ2lvbi5vcmllbnRhdGlvbgoKCnAudGltZTJtcmNhLnN1YmxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IlRQQSBMaW5lYWdlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKcC50aW1lMm1yY2Euc3VibGluZWFnZQoKCnAudGltZTJtcmNhLkxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBX0xpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBX0xpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIChNZWRpYW4gb2YgUG9zdGVyaW9yKSIsIGNvbG9yPSJUUEEgTGluZWFnZSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKQpgYGAKXApcCk1heWJlIGNhbiBtYWtlIGFuIE1TVCBvZiB0aGUgTm9ydGggRWFzdCBzYW1wbGVzIGZvciBncmFwZXRyZWU/CmBgYHtyfQpUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0IDwtIGFwZTo6a2VlcC50aXAoVFBBLnB5amFyLnRyZWUsIGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiU2FtcGxlX05hbWUiXSkpKQoKI2dndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0KQojd3JpdGUudHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0LCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LU5vcnRoRWFzdC5weWphci4yMDIyLTAyLTI2LnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKCkFsdGVybmF0aXZlIGFwcHJvYWNoIHVzaW5nIE1TVCBpbnN0ZWFkIG9mIG5ldHdvcmtzIGZvciBOb3J0aCBFYXN0IGRhdGEKYGBge3J9CiMgUmVhZCBpbiBNU1QKI1RQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLU5vcnRoRWFzdC0yMDIyLTAyLTI2LkdlbmRlck9yaWVudGF0aW9uLU1TVHJlZS5pbmtzY2FwZWQuK25vZGUtY291bnRzK0dCTVNNLnN2ZyIpCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUpCnAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIgPC0gcGxvdF9ncmlkKHAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLCBsYWJlbHM9YygiQSAtIE5ldHdvcmsgQ2x1c3RlcnMgKE5vcnRoIEVhc3QgRW5nbGFuZCkiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSkKCmBgYApcClBsb3Qgd2l0aCBiZWFzdCB0cmVlcwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQojcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDMsNiksIGxhYmVscz1jKCJBIC0gTmV0d29yayBDbHVzdGVycyAoTm9ydGggRWFzdCBFbmdsYW5kKSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGUgPSAwLjk1KQoKcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTMsIG5jb2w9MSwgcmVsX2hlaWdodHM9YygzLDcpKQoKCgpwLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmkKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzNfU3VibGluMS5Ob3J0aEVhc3QuTVNUK0JlYXN0LiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTIwMCwgaGVpZ2h0PTI0NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCiNnZ3NhdmUocGxvdD1wLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmksIHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiRmlnM19TdWJsaW4xLk5vcnRoRWFzdC5NU1QrQmVhc3QuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5zdmciKSwgdW5pdHM9J21tJywgd2lkdGg9MjAwLCBoZWlnaHQ9MjQ1LCBkZXZpY2U9c3ZnbGl0ZSwgZHBpPTEyMDApCgoKYGBgCgpcCkRvIHNvbWUgYW5hbHlzaXMgb2YgbWFqb3Igc3VibGluZWFnZXMgb3ZlciB0aW1lIGJ5IHJlZ2lvbiAtIGNvdWxkIHRoaXMgaW5mbHVlbmNlIG9ic2VydmF0aW9ucyBhYm91dCBzdWJsaW5lYWdlcz8KYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTR9CiMgR2VuZXJhdGUgc29tZSBzdGF0cyBieSBQSEUgUmVnaW9uClBIRS5tYWpvci5zdWJsaW5lYWdlLlBIRWNlbnRyZS5kYXRlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgJWluJSBjKDEsMTQpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHBoZV9jZW50cmUsIHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHBoZV9jZW50cmUpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCgpnZ3Bsb3QoUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUsIGFlcyh5ZWFyLCBwaGVfY2VudHJlLCBzaXplPUNvdW50LCBjb2xvcj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KCkgKyAKICBmYWNldF9ncmlkKC5+VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKQoKCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUuYnViYmxlcGxvdCA8LSBnZ3Bsb3QoUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUsIGFlcyh5ZWFyLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgY29sb3I9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjY1LCBhZXMoc2l6ZT1Db3VudCkpICsgCiAgZ2VvbV9saW5lKGFscGhhPTAuMjUpICsKICBmYWNldF9ncmlkKGZhY3Rvcihnc3ViKCJcXCAiLCJcbiIscGhlX2NlbnRyZSksIGxldmVscz1nc3ViKCJcXCAiLCJcbiIsUEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSl+Liwgc3dpdGNoPSd5JykgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIikgKwogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9J3doaXRlJywgZmlsbD0nd2hpdGUnLGxpbmV0eXBlPSJzb2xpZCIpLCBzdHJpcC50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXkyNSIsYW5nbGU9MCwgc2l6ZT01KSkgKyAKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA0LjUsYnJlYWtzPWMoMSw1LDEwLDIwLDMwLDQwKSkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpICsgCiAgbGFicyh5PSJSZWdpb24iLCB4PSJZZWFyIiwgY29sb3I9IlN1YmxpbmVhZ2UiKSAKIApwLlBIRS5tYWpvci5zdWJsaW5lYWdlLlBIRWNlbnRyZS5kYXRlLmJ1YmJsZXBsb3QKCmBgYApcCkRvIHNvbWUgc3BlY2lmaWMgYW5hbHlzaXMgZm9yIHRoZSAzIE5vcnRoZXJuIHJlZ2lvbnMKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTN9CiMgR2VuZXJhdGUgc29tZSBzdGF0cyBieSBQSEUgUmVnaW9uCiBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudD1uKCkpCgogUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihwaGVfY2VudHJlICVpbiUgYygiTm9ydGggRWFzdCIsICJOb3J0aCBXZXN0IiwgIllvcmtzaGlyZSBhbmQgSHVtYmVyIikpICU+JQogIGRwbHlyOjpncm91cF9ieSh5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkKCgpwLlBIRS5tYWpvci5zdWJsaW5lYWdlLjNOb3J0aGVyblJlZ2lvbnMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihwaGVfY2VudHJlICVpbiUgYygiTm9ydGggRWFzdCIsICJOb3J0aCBXZXN0IiwgIllvcmtzaGlyZSBhbmQgSHVtYmVyIikpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgeWVhciwgcGhlX2NlbnRyZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGdncGxvdChhZXMoeWVhciwgQ291bnQsIGZpbGw9cGhlX2NlbnRyZSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCB3aWR0aD0wLjY1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pICsKICB0aGVtZV9idygpICsgdGhlbWUudGV4dC5zaXplICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgyMDEyLDIwMTgsMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXByZXR0eSkgKwogIGxhYnModGl0bGU9IlNhbXBsZXMgaW4gMyBOb3J0aGVybiBSZWdpb25zIiwgeD0iQ29sbGVjdGlvbiBZZWFyIiwgeT0iU2FtcGxlIENvdW50IiwgZmlsbD0iUHVibGljIEhlYWx0aFxuUmVnaW9uIikgKwogIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgI2dlb21fdGV4dChhZXMoeD15ZWFyLHk9Q291bnQtMC41LCBsYWJlbD1Db3VudCksIGNvbG9yPSdncmV5OTUnLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsKICBOVUxMCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuM05vcnRoZXJuUmVnaW9ucwoKYGBgCgoKClwKU2luZ2xlIGxpbmthZ2UgbmV0d29yayBvZiBpZGVudGljYWwgZ2Vub21lcyBmcm9tIFVLCgpgYGB7cn0KIyBDb25zdHJhaW4gYnkgU05QIGRpc3RhbmNlIChpZGVudGljYWwgaW4gdGhlIGFzciBzbnAgdHJlZSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhW1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSREaXN0YW5jZS5QaHlsbz09MCxdCgojIGFuZCBhIG1heCBvZiAyIHllYXJzCiNQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTIsXQoKCiMgQW5kIG1ha2Ugc3VyZSB0aGF0IHdlIGFjdHVhbGx5IGhhdmUgZ2VuZXRpYyBkaXN0YW5jZSBkYXRhIGZvciBhbGwgc2FtcGxlcyB3aXRoaW4gdGhlIG5ldHdvcmsKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJERpc3RhbmNlLlBoeWxvKSxdCgojIHJlbW92ZSBzZWxmLXNhbXBsZXMKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMkc2FtZS5zYW1wbGU9PSJkaWZmZXJlbnQiLF0KCgojIGNsZWFudXAgc29tZSBkYXRhIG5vaXNlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyR5ZWFyLnQxKSxdCgojIHByZXBhcmUgaW50cHV0IGRhdGEgKHdpdGggZWRnZSBpbmZvKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbLGMoIlRheGExIiwiVGF4YTIiLCJEaXN0YW5jZS5QaHlsbyIsImRlY2ltYWwuZGF0ZS5kaXN0YW5jZSIsInllYXIuZGlzdGFuY2UiLCJPcmllbnRhdGlvbi5DbGFzcyIsImVwaS50aW1lLmRpc3RhbmNlLmNhdC55ZWFycyIsImVwaS50aW1lLmRpc3RhbmNlLmNhdCIpXQoKIyMjIyMjIyMjIyMjCiMgc29tZSBpc3N1ZXMgd2l0aCB1cGRhdGUgdG8gUjQgLSBkb3VibGUgc2lkZWQgbWF0cml4ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRlZGdlbmFtZSA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSksIGZ1bmN0aW9uKHgpIHBhc3RlMChzb3J0KGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxW3gsYygiVGF4YTEiLCJUYXhhMiIpXSkpKSxjb2xsYXBzZT0iX19fIikpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDFbIWR1cGxpY2F0ZWQoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGVkZ2VuYW1lKSxdCgojIEFsc28gaGF2aW5nIGFuIGlzc3VlIHdpdGggdGF4YSBhcyBmYWN0b3JzIGhlcmUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJFRheGExIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTEpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRUYXhhMiA8LSBhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJFRheGEyKQojIyMjIyMjIyMjIyMKIyBEZWR1cGxpY2F0ZQoKI2ludmVyc2Ugd2VpZ2h0ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRkZWNpbWFsLmRhdGUuZGlzdGFuY2UuaW52IDwtIDEvMS8oUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZSswLjA0KQoKIyBNYWtlIGFjdHVhbCBuZXR3b3JrCnNldC5zZWVkKDEyMzYpClBIRS5pZGVudGljYWxzLm5ldHdvcmsgPC0gbmV0d29yayhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEsIG1hdHJpeC50eXBlID0gImVkZ2VsaXN0IiwgaWdub3JlLmV2YWwgPSBGQUxTRSwgZGlyZWN0ZWQgPSBGLCBsb29wcyA9IEYpCgojUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLmlkZW50aWNhbHMubmV0d29yaywgbGF5b3V0ID0gImthbWFkYWthd2FpIiwgd2VpZ2h0cyA9ICJkZWNpbWFsLmRhdGUuZGlzdGFuY2UuaW52IikKI1BIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gZ2duZXR3b3JrKFBIRS5pZGVudGljYWxzLm5ldHdvcmssIGxheW91dCA9ICJmcnVjaHRlcm1hbnJlaW5nb2xkIiwgd2VpZ2h0cyA9ICJkZWNpbWFsLmRhdGUuZGlzdGFuY2UiKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIGdnbmV0d29yayhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLCBsYXlvdXQgPSAiZnJ1Y2h0ZXJtYW5yZWluZ29sZCIpCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJFRheGExIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckdmVydGV4Lm5hbWVzCgojIGV4dHJhY3QgdGVtcG9yYWwgY2x1c3RlcnMgZnJvbSBuZXR3b3JrClBIRS5pZGVudGljYWxzLm5ldHdvcmsuaWcgPC0gYXNJZ3JhcGgoUEhFLmlkZW50aWNhbHMubmV0d29yaykKUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzIDwtIGRhdGEuZnJhbWUoVGF4YTE9bmV0d29yay52ZXJ0ZXgubmFtZXMoUEhFLmlkZW50aWNhbHMubmV0d29yayksIHZlcnRleC5ubz1hcy52ZWN0b3IoVihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmlnKSksIGNsdXN0ZXI9aWdyYXBoOjpjb21wb25lbnRzKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuaWcpJG1lbWJlcnNoaXApClBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyIDwtIHBhc3RlMCgiQ2x1c3RlciIsUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJGNsdXN0ZXIpCgojIG1lcmdlIG1ldGFkYXRhIGJhY2sgaW4KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2csIGRhdGEuZnJhbWUoVGF4YTE9UEhFLm1ldGFkYXRhLmxpbmtlZCRTYW1wbGVfTmFtZSwgUEhFLm1ldGFkYXRhLmxpbmtlZFssYygicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBzdHJpbmdzQXNGYWN0b3JzID0gRiksYnk9IlRheGExIiwgdHlwZT0ibGVmdCIpCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXI9UEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpLCBieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKCgojIAojIEFkZCB0ZW1wb3JhbCBjb2xvdXIgc2NhbGUKI3VuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJGVwaS50aW1lLmRpc3RhbmNlLmNhdCkKCmVwaS50aW1lLmRpc3RhbmNlLmNhdC5jb2xzIDwtIHJldihjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOCwgIkdyZXlzIikpKGxlbmd0aCh1bmlxdWUoUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyRlcGkudGltZS5kaXN0YW5jZS5jYXQpKS0xKSkKCgojIFBsb3QgbmV0d29yawpwLlBIRS5pZGVudGljYWxzLm5ldHdvcmsuMFNOUCA8LSBnZ3Bsb3QoUEhFLmlkZW50aWNhbHMubmV0d29yay5nZywgYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSkgKyAKICBnZW9tX2VkZ2VzKGFscGhhPTAuOTAsIGN1cnZhdHVyZSA9IDAuMiwgYWVzKGNvbG9yPWZhY3RvcihlcGkudGltZS5kaXN0YW5jZS5jYXQpLCBsaW5ldHlwZT1mYWN0b3IoZXBpLnRpbWUuZGlzdGFuY2UuY2F0KSkpICsKICAjc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmV5NSIsImdyZXkzNSIsImdyZXk1NSIsICJncmV5NjUiLCAiZ3JleTc1IiksIG5hbWU9IlNOUFxuRGlzdGFuY2UiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlRlbXBvcmFsXG5EaXN0YW5jZSIsIHZhbHVlcyA9IGVwaS50aW1lLmRpc3RhbmNlLmNhdC5jb2xzKSArCiAgc2NhbGVfbGluZXR5cGUobmFtZT0iVGVtcG9yYWxcbkRpc3RhbmNlIikgKwogIHRoZW1lX2JsYW5rKCkgKwogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsgZ2duZXdzY2FsZTo6bmV3X3NjYWxlKCJzaXplIikgKwogICNnZW9tX25vZGVsYWJlbChhZXMoY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uLCBsYWJlbD1wYXN0ZShUYXhhMSx5ZWFyLHNlcD0iXG4iKSxmb250ZmFjZSA9ICJib2xkIiksIGFscGhhPTAuOCwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLTAuNCwgbGFiZWwuc2l6ZT0wLjE1LCBsYWJlbC5wYWRkaW5nID0gdW5pdCgwLjA1LCAibGluZXMiKSkgKwogIGdlb21fbm9kZXMoc2l6ZT0yLjUsIGFlcyhjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24pLCBhbHBoYT0wLjkpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKSArIAogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0ncmlnaHQnKSArCiAgTlVMTApwLlBIRS5pZGVudGljYWxzLm5ldHdvcmsuMFNOUAoKYGBgCgpQbG90IHRoaXMgYWdhaW5zdCBhIFVLIHRyZWU/CmBgYHtyfQpnaGVhdG1hcChnZ3RyZWUoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrKSwKZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVyPVBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSkKCmBgYAoKCgpcClNvbWUgc3RhdHMgZnJvbSB0aGlzCmBgYHtyfQpwLlBIRS5pZGVudGljYWwuT3JpZW50YXRpb25fY2xhc3MuYnlkYXRlZGlzdCA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgJT4lCiAgZHBseXI6OmZpbHRlcihzYW1lLnNhbXBsZT09ImRpZmZlcmVudCIsIERpc3RhbmNlLlBoeWxvPT0wKSAlPiUKICAjZmlsdGVyKGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGVwaS50aW1lLmRpc3RhbmNlLmNhdCwgT3JpZW50YXRpb24uQ2xhc3MpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmNsYXNzLmRhdGU9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN1bS5jbGFzcz1zdW0oQ291bnQuY2xhc3MuZGF0ZSksIGZyYWN0LmNsYXNzPUNvdW50LmNsYXNzLmRhdGUvc3VtLmNsYXNzKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCB5PUNvdW50LmNsYXNzLmRhdGUsIGZpbGw9T3JpZW50YXRpb24uQ2xhc3MpKSArCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBwb3NpdGlvbj0nc3RhY2snKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBsYWJzKHg9IlRpbWUgYmV0d2VlbiBzYW1wbGVzIiwgeT0iSW50ZXJhY3Rpb24gQ291bnQiLCBmaWxsPSJPcmllbnRhdGlvbiBUeXBlIikKcC5QSEUuaWRlbnRpY2FsLk9yaWVudGF0aW9uX2NsYXNzLmJ5ZGF0ZWRpc3QKCgogIApwLlBIRS5pZGVudGljYWwuT3JpZW50YXRpb25fY2xhc3MuYnlaZXJvZGlzdC5jbHVzdGVyIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgJT4lCiAgZHBseXI6OmZpbHRlcighaXMubmEoT3JpZW50YXRpb24uQ2xhc3MpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3RlciwgT3JpZW50YXRpb24uQ2xhc3MpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmNsYXNzLmNsdXN0ZXI9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN1bS5jbGFzcz1zdW0oQ291bnQuY2xhc3MuY2x1c3RlciksIGZyYWN0LmNsYXNzPUNvdW50LmNsYXNzLmNsdXN0ZXIvc3VtLmNsYXNzKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHN1bS5jbGFzcykpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyPWFzX2ZhY3RvcihDbHVzdGVyKSkgJT4lCiAgZ2dwbG90KGFlcyh4PUNsdXN0ZXIsIHk9Q291bnQuY2xhc3MuY2x1c3RlciwgZmlsbD1PcmllbnRhdGlvbi5DbGFzcykpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdzdGFjaycpICsgCiAgdGhlbWVfYncoKSArCiAgeC50aGVtZS5heGlzLnJvdGF0ZSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBsYWJzKHg9IklkZW50aWNhbCBHZW5vbWUgQ2x1c3RlciIsIHk9IkludGVyYWN0aW9uIENvdW50IiwgZmlsbD0iT3JpZW50YXRpb24gVHlwZSIpCnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieVplcm9kaXN0LmNsdXN0ZXIKCmQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gbGVmdF9qb2luKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50c1ssYygiVGF4YTEiLCJDbHVzdGVyIildLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInBoZV9jZW50cmUiLCJsb25kb24iLCJ5ZWFyIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCJUUEFfTGluZWFnZSIpXSwgYnk9YygiVGF4YTEiPSJTYW1wbGVfTmFtZSIpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIENsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5vcmllbnQuY2x1c3Rlcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoY291bnQuY2x1c3Rlcj1zdW0oY291bnQub3JpZW50LmNsdXN0ZXIpLCBmcmFjdD1jb3VudC5vcmllbnQuY2x1c3Rlci9jb3VudC5jbHVzdGVyKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoY291bnQuY2x1c3RlcikpICU+JQogIGRwbHlyOjptdXRhdGUoQ2x1c3Rlci5vPWFzX2ZhY3RvcihDbHVzdGVyKSkKCmQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIKCiMgUGxvdCBzYW1wbGUgY291bnRzIGJ5IGdlbm9tZSBjbHVzdGVyIChjb2xvdXJlZCBieSBvcmllbnRhdGlvbikKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5vcmllbnQuY2x1c3RlciwgZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC42NSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgeC50aGVtZS5heGlzLnJvdGF0ZSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsNDUsNSkpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iU2FtcGxlIENvdW50IiwgZmlsbD0iUGF0aWVudCBHZW5kZXIgT3JpZW50YXRpb24iKSAKCiMgQWRkIGRldGFpbHMgb2Ygc3VibGluZWFnZSAgCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciArIAogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGE9KGQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIHNlbGVjdChDbHVzdGVyLm8sIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUgZGlzdGluY3QoKSksIGFlcyhDbHVzdGVyLm8sIC0xLjUsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgaW5oZXJpdC5hZXMgPSBGKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBuYW1lPSJTdWJsaW5lYWdlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIE5VTEwKCiMgQWRkIGEgc3VibGluZWFnZSBheGlzIGxhYmVsIChiaXQgb2YgYSBoYWNrKQpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZW9tX3RleHQoZGF0YT1kYXRhLmZyYW1lKGxhYj0iU3VibGluZWFnZSIsIHk9LTEuNSwgeD0yOCwgc3RyaW5nc0FzRmFjdG9ycz1GKSwgYWVzKGxhYmVsPWxhYiwgeD14LCB5PXkpLCBoanVzdCA9IDAuMSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBjb29yZF9jYXJ0ZXNpYW4oeD1jKDEsIDI3KSwgY2xpcD0nb2ZmJykKICAKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKIyMjIyMjZ3h4eHhnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKXApQb3NzaWJsZSB0byBpbnRyb2R1Y2Ugc29tZSBtb3JlIGluZm8gaW50byB0aGF0IHBsb3Q/CgoKYGBge3J9CmQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGxlZnRfam9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHNbLGMoIlRheGExIiwiQ2x1c3RlciIpXSwgUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIGJ5PWMoIlRheGExIj0iU2FtcGxlX05hbWUiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBDbHVzdGVyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnJlZ2lvbi5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5yZWdpb24uY2x1c3RlciksIGZyYWN0PWNvdW50LnJlZ2lvbi5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCnAuUEhFLmlkZW50aWNhbC5SZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5yZWdpb24uY2x1c3RlciwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTAuNjUsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHgudGhlbWUuYXhpcy5yb3RhdGUgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDQ1LDUpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iUmVnaW9uIFByb3BvcnRpb24iLCBmaWxsPSJVS0hTQSBSZWdpb24iKSAKCgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwLlBIRS5pZGVudGljYWwuYnlaZXJvZGlzdC5jbHVzdGVyLmJhcmNvbWJpIDwtIHBsb3RfZ3JpZChwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgeC50aGVtZS5zdHJpcCwgcC5QSEUuaWRlbnRpY2FsLlJlZ2lvbi5ieVplcm9kaXN0LmNsdXN0ZXIsIG5jb2w9MSwgYXhpcz0icmx0IiwgYWxpZ249VCwgcmVsX2hlaWdodHMgPSBjKDIsMSksIGxhYmVscz1jKCJCIiwiQyIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKI3AuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkKI3AuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpwbG90X2dyaWQocC5QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLjBTTlAsIHAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmksIG5jb2w9MSwgcmVsX2hlaWdodHM9YygyLDMpLCBsYWJlbHM9YygiQSIsIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKCnAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkubm9OZXQgPC0gcGxvdF9ncmlkKHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyB4LnRoZW1lLnN0cmlwLCBwLlBIRS5pZGVudGljYWwuUmVnaW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciwgbmNvbD0xLCBheGlzPSJybHQiLCBhbGlnbj1ULCByZWxfaGVpZ2h0cyA9IGMoMiwxKSwgbGFiZWxzPWMoIkEiLCJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCnAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkubm9OZXQgCgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMjAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKCgpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5yZWdpb24uc2NhdHRlcnBpZS5ncm91cHMgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6c2VsZWN0KENsdXN0ZXIsIFRheGExLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3RlciwgcGhlX2NlbnRyZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2VudHJlPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh4PUNsdXN0ZXIsIHk9My41KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPSJwaGVfY2VudHJlIiwgdmFsdWVzX2Zyb209IkNvdW50LmNlbnRyZSIsIHZhbHVlc19maWxsPTApICU+JQogIGRwbHlyOjpzZWxlY3QoQ2x1c3Rlcix4LHksdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckcGhlX2NlbnRyZSkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm51bWVyaWM9YXMubnVtZXJpYygxOjI3KSkKICAKCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpICMrCiAgCgpgYGAKCgoKXApHZXQgYSBmZXcgbW9yZSBzdGF0cyBvbiB0aGUgbGFyZ2VzdCBjbHVzdGVyIChDbHVzdGVyIDgpCmBgYHtyfQojZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciAlPiUgZmlsdGVyKENsdXN0ZXI9PSJDbHVzdGVyOCIpCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbC5jbHVzdGVyOCA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3Rlcj09IkNsdXN0ZXI4IikgICU+JQogIHNlbGVjdCh2ZXJ0ZXgubmFtZXMsIE9yaWVudGF0aW9uLkNsYXNzLCBwaGVfY2VudHJlLCB5ZWFyLCBUUEFfTGluZWFnZSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGhpdnBvcywgQ2x1c3RlcikKCnNvcnQodW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsLmNsdXN0ZXI4JHllYXIpKQoKYGBgCgpcCkdldCBzb21lIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGhldGVyb3NleHVhbCBvbmx5IGNsdXN0ZXJzCmBgYHtyfQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9oZXRlcm9jbHVzdGVycyA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3RlciAlaW4lIGMoIkNsdXN0ZXIxMiIsICJDbHVzdGVyMjAiLCAiQ2x1c3RlcjI3IikpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBDbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIHBoZV9jZW50cmUsIHllYXIsIFRQQV9MaW5lYWdlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKENsdXN0ZXIsIHllYXIsIGdlbmRlcl9vcmllbnRhdGlvbikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsX2hldGVyb2NsdXN0ZXJzCmBgYApcIApBbmQgZG8gdGhlIHNhbWUgZm9yIHRoZSBzbWFsbCBtaXhlZC9HQk1TTSBjbHVzdGVycwpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfbm90LmhldGVyb2NsdXN0ZXJzIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgJT4lIGZpbHRlcihDbHVzdGVyICVub3RpbiUgYygiQ2x1c3RlcjEyIiwgIkNsdXN0ZXIyMCIsICJDbHVzdGVyMjciLCAiQ2x1c3RlcjgiKSkgICU+JQogIHNlbGVjdCh2ZXJ0ZXgubmFtZXMsIENsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbiwgcGhlX2NlbnRyZSwgeWVhciwgVFBBX0xpbmVhZ2UsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBoaXZwb3MpICU+JSAKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoQ2x1c3RlciwgeWVhciwgZ2VuZGVyX29yaWVudGF0aW9uKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9ub3QuaGV0ZXJvY2x1c3RlcnMKYGBgCgoKCldoYXQgcHJvcG9ydGlvbiBvZiBoZXRlcm9zZXh1YWxzIGhhdmUgYW4gaWRlbnRpY2FsIEdCTVNNIHBhaXJlZCBnZW5vbWU/ClwKYGBge3J9CgojIERlbGluZWF0ZSBoZXRlcm9zZXh1YWwgY2x1c3RlcnMKZC5QSEUuaWRlbnRpY2FsLmhldGVyb3NleHVhbC5jbHVzdGVycyA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JSAKICBkcGx5cjo6bXV0YXRlKGlzLmhldGVyb3NleHVhbD1pZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uJWluJSBjKCJNU1ciLCAiV1NNIiksICJoZXRlcm9zZXh1YWwiLCBpZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uPT0iR0JNU00iLCJHQk1TTSIsICJVbmtub3duIikpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoQ2x1c3Rlcixpcy5oZXRlcm9zZXh1YWwpICU+JSAKICBkcGx5cjo6bXV0YXRlKGNvdW50LmhldGVybz1zdW0oY291bnQub3JpZW50LmNsdXN0ZXIpLCBmcmFjdC5oZXRlcm89c3VtKGNvdW50Lm9yaWVudC5jbHVzdGVyKS9jb3VudC5jbHVzdGVyKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjpmaWx0ZXIoaXMuaGV0ZXJvc2V4dWFsPT0iaGV0ZXJvc2V4dWFsIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWMoY291bnQub3JpZW50LmNsdXN0ZXIsIGdlbmRlcl9vcmllbnRhdGlvbiwgZnJhY3QpKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNsdXN0ZXIudHlwZT1pZmVsc2UoZnJhY3QuaGV0ZXJvPT0xLCAiaGV0ZXJvLm9ubHkiLCAib3RoZXIiKSkKCmQuUEhFLmlkZW50aWNhbC5oZXRlcm9zZXh1YWwuY2x1c3RlcnMgCgojIFdoYXQgcHJvcG9ydGlvbiBvZiBoZXRlcm9zZXh1YWxzIChuPTIwKSBhcmUgaW4gYSBoZXRlcm9zZXh1YWwtb25seSBjbHVzdGVyPwpkLlBIRS5pZGVudGljYWwuaGV0ZXJvc2V4dWFsLmNsdXN0ZXJzICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoY2x1c3Rlci50eXBlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LmluLmhldGVyby5jbHVzdGVyPXN1bShjb3VudC5oZXRlcm8pKSAlPiUgCiAgZHBseXI6Om11dGF0ZShmcmFjdC5pbi5oZXRlcm89Y291bnQuaW4uaGV0ZXJvLmNsdXN0ZXIvc3VtKGNvdW50LmluLmhldGVyby5jbHVzdGVyKSkKICAKCiNsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpCmBgYAoKXAoKCiMgUmV2aXNpb25zIDAzLTIwMjMgb253YXJkcwoKXApMb29rIGF0IHByb3BvcnRpb24gb2YgZ2Vub21lcyBhdCBkaWZmZXJlbnQgY292ZXJhZ2UgdGhyZXNob2xkcwpgYGB7cn0KIyBDdW11bGF0aXZlIHByb3BvcnRpb24gb2YgTiBjb3VudHMgaW4gZ2Vub21lcwpQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLlVLIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoaXMuVUs9PSJVSyIpICU+JQogIGRwbHlyOjpncm91cF9ieShgUHJvcG9ydGlvbi1OXz41X21hcHBpbmcrbWFza2luZ19OaWNob2xzYCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuQ291bnQ9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5Db3VudCwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9jb3VudD1jdW1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKERhdGFzZXQ9IlVLIChuPTIzNykiKQpQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLlVLCgoKUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5BTEwgPC0gVFBBLm1ldGEyLjEgJT4lIAogIGRwbHlyOjpmaWx0ZXIoZnVsbC50ZW1wb3JhbC5hbmFseXNpcz09IlllcyIpICU+JQogIGRwbHlyOjpncm91cF9ieShgUHJvcG9ydGlvbi1OXz41X21hcHBpbmcrbWFza2luZ19OaWNob2xzYCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuQ291bnQ9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5Db3VudCwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9jb3VudD1jdW1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6bXV0YXRlKERhdGFzZXQ9IkFsbCAobj01MjApIikgClBIRS5tZXRhZGF0YS5OY291bnQuY3VtbXVsYXRpdmUuQUxMClBIRS5tZXRhZGF0YS5OY291bnQuY3VtbXVsYXRpdmUuY29tYmkgPC0gcmJpbmQoUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5VSywgUEhFLm1ldGFkYXRhLk5jb3VudC5jdW1tdWxhdGl2ZS5BTEwpCgoKCgpwLmN1bXVsYXRpdmUuTmNvdW50LmZvci5kYXRzZXQgPC0gIGdncGxvdChQSEUubWV0YWRhdGEuTmNvdW50LmN1bW11bGF0aXZlLmNvbWJpICwgYWVzKGBQcm9wb3J0aW9uLU5fPjVfbWFwcGluZyttYXNraW5nX05pY2hvbHNgLCBjdW1fZnJhY3QsIGdyb3VwPURhdGFzZXQsIGNvbG9yPURhdGFzZXQpKSArIAogIGdlb21fcG9pbnQoYWxwaGE9MC43NSwgc2l6ZT0xKSArCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICd0b3AnKSArCiAgbGFicyh5PSJDdW11bGF0aXZlIGZyYWN0aW9uIG9mIGdlbm9tZXMiLCB4PSJQcm9wb3J0aW9uIG9mIHNpdGVzIG1hc2tlZCB0byBOIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMSwwLjEpKQoKcC5jdW11bGF0aXZlLk5jb3VudC5mb3IuZGF0c2V0CmBgYAoKXApCRUFTVCA5NSUgSFBEIGNhbGN1bGF0aW9ucyAocHJvdmlkZSBtb3JlIGRldGFpbHMgZm9yIDUyMCBkYXRhc2V0ICAgICkKYGBge3J9CkJFQVNULm1lZGlhbiA8LSAxLjI4ZS03CkJFQVNULjk1SFBEIDwtIGMoMS4wN2UtNywgMS40OGUtNykKU1MxNC5hbG4ubGVuZ3RoIDwtIDExMzk1NjkKCgoxLyhCRUFTVC5tZWRpYW4gKiBTUzE0LmFsbi5sZW5ndGgpCjEvKEJFQVNULjk1SFBEICogU1MxNC5hbG4ubGVuZ3RoKQpgYGAKClwKXApGdXJ0aGVyIGV2YWx1YXRpb24gb2Ygc3VibGluZWFnZSA2IChyZXZpZXdlciByZXNwb25zZSkgdXNpbmcgYW5jZXN0cmFsIHJlY29uc3RydWN0aW9uIHBlcmZvcm1lZCBvbiB0aGUgZ2xvYmFsIFRQQS1vbmx5IGFsaWdubWVudC90cmVlIHVzZWQgaW4gQmVhbGUgMjAyMS4KCmBgYHtyfQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUgPC0gcmVhZC5uZXh1cyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUuZmlsZSkKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC50cmVlLmRhdGEgPC0gZm9ydGlmeShUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnRyZWUpCgpnZ3RyZWUoVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC50cmVlKSArIGdlb21fbm9kZWxhYihzaXplPTIpCgojIFJlYWQgaW4gYW5kIHByb2Nlc3MgVFBBLW9ubHkgdmNmICh0byBjb25maXJtIHNpdGVzIGFyZSB0aGUgc2FtZSkKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZiA8LSByZWFkLnZjZlIoVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZmlsZSwgdmVyYm9zZSA9IEZBTFNFKQpUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCA8LSBnZXRGSVgoVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZikKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXggPC0gZGF0YS5mcmFtZShUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeFssYygyLDQsNSldLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXgkaW4uVFBBLm9ubHkgPC0gInllcyIKVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXgkS2V5IDwtIDE6bnJvdyhUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCkKCmBgYAoKXApFeHRyYWN0IGdlbm90eXBlIHNpdGVzCmBgYHtyfQpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndCA8LSBleHRyYWN0X2d0X3RpZHkoVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZikKClRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmYgPC0gcGx5cjo6am9pbihUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndCwgVFBBLm9ubHkubWlkcG9pbnQudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5maXhbLGMoIktleSIsIlBPUyIpXSwgYnk9IktleSIsIHR5cGU9ImxlZnQiKQoKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZ3QuZiRQT1MgPC0gYXMubnVtZXJpYyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mJFBPUykKVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZ3QuZiRndF9HVCA8LSBhcy5udW1lcmljKFRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmYkZ3RfR1QpCgpUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCA8LSB0aWR5cjo6c3ByZWFkKFRQQS50cmVldGltZS5hbmNlc3RyYWwudmNmLmd0LmZbLGMoIlBPUyIsIkluZGl2IiwiZ3RfR1QiKV0sIFBPUywgZ3RfR1QpIAoKYGBgCgpVc2Ugc25wRWZmIHRvIGFubm90YXRlIG11bHRpLXZjZiwgYW5kIHRoZW4gcHVsbCBpbiBhbm5vdGF0aW9ucyBoZXJlCmBgYHtyfQpUUEEuc25wRWZmIDwtIHJlYWQudGFibGUoVFBBLnNucEVmZi5maWxlLGhlYWRlciA9IFQsIGNoZWNrLm5hbWVzID0gRiwgY29tbWVudC5jaGFyID0gIiIsc2VwPSJcdCIpCgpUUEEuc25wRWZmLmZpbHQgPC0gVFBBLnNucEVmZlshKFRQQS5zbnBFZmYkYEFOTlsqXS5HRU5FYD09ImdlbmUtVFBBU1NfUlMwMDA0MCIgJiBUUEEuc25wRWZmJGBBTk5bKl0uRUZGRUNUYD09ImludHJhZ2VuaWNfdmFyaWFudCIpLF0KVFBBLnNucEVmZi5maWx0W1RQQS5zbnBFZmYuZmlsdCRgQU5OWypdLkVGRkVDVGA9PSIuIiwiQU5OWypdLkVGRkVDVCJdIDwtICJpbnRyYWdlbmljX3ZhcmlhbnQiCgoKVFBBLnNucEVmZi5maWx0ICU+JSBkcGx5cjo6Z3JvdXBfYnkoYEFOTlsqXS5FRkZFQ1RgKSAlPiUgc3VtbWFyaXNlKENvdW50PW4oKSkKVFBBLnNucEVmZi5maWx0ICU+JSBkcGx5cjo6Z3JvdXBfYnkoYEFOTlsqXS5HRU5FYCkgJT4lIHN1bW1hcmlzZShDb3VudD1uKCkpClRQQS5zbnBFZmYuZmlsdCAlPiUgZHBseXI6Omdyb3VwX2J5KGBBTk5bKl0uR0VORWAsYEFOTlsqXS5FRkZFQ1RgKSAlPiUgc3VtbWFyaXNlKENvdW50PW4oKSkKClRQQS5zbnBFZmYuZmlsdC52YXIucGVyLnBvcyA8LSBUUEEuc25wRWZmLmZpbHQgJT4lIGRwbHlyOjpncm91cF9ieShQT1MpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKQpUUEEuc25wRWZmLmZpbHQudmFyLnBlci5wb3MubXVsdGkgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIodW5saXN0KFRQQS5zbnBFZmYuZmlsdC52YXIucGVyLnBvc1tUUEEuc25wRWZmLmZpbHQudmFyLnBlci5wb3MkQ291bnQ+MSwiUE9TIl0pKSkKClRQQS5zbnBFZmYuZmlsdFtUUEEuc25wRWZmLmZpbHQkUE9TICVpbiUgVFBBLnNucEVmZi5maWx0LnZhci5wZXIucG9zLm11bHRpLF0KCmBgYAoKXApMZXRzIHB1bGwgaW4gZ2VuZSBmdW5jdGlvbiAod2hlcmUga25vd24pIGZvciB0aGVzZSBzaXRlcyBmcm9tIHRoZSBnZmYKYGBge3J9ClNTMTQuZ2ZmIDwtIGFwZTo6cmVhZC5nZmYoU1MxNC5nZmYuZmlsZSkKU1MxNC5nZmYuY2RzIDwtIFNTMTQuZ2ZmW1NTMTQuZ2ZmJHR5cGU9PSJDRFMiLF0KCiMjIyMgZnVuY3Rpb24gdG8gZXh0cmFjdCBkaWZmZXJlbnQgZmllbGRzIGZyb20gYXR0cmlidXRlcyBjb2x1bW4KZ2V0QXR0cmlidXRlRmllbGQgPC0gZnVuY3Rpb24gKHgsIGZpZWxkLCBhdHRyc2VwID0gIjsiKSB7CiAgICAgcyA9IHN0cnNwbGl0KHgsIHNwbGl0ID0gYXR0cnNlcCwgZml4ZWQgPSBUUlVFKQogICAgIHNhcHBseShzLCBmdW5jdGlvbihhdHRzKSB7CiAgICAgICAgIGEgPSBzdHJzcGxpdChhdHRzLCBzcGxpdCA9ICI9IiwgZml4ZWQgPSBUUlVFKQogICAgICAgICBtID0gbWF0Y2goZmllbGQsIHNhcHBseShhLCAiWyIsIDEpKQogICAgICAgICBpZiAoIWlzLm5hKG0pKSB7CiAgICAgICAgICAgICBydiA9IGFbW21dXVsyXQogICAgICAgICB9CiAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgcnYgPSBhcy5jaGFyYWN0ZXIoTkEpCiAgICAgICAgIH0KICAgICAgICAgcmV0dXJuKHJ2KQogICAgIH0pCn0KIyMjCiNnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgIk5hbWUiKQoKCiMgRXh0cmFjdCBhdHRyaWJ1dGUgZWxlbWVudHMgZnJvbSBnZmYKU1MxNC5nZmYuY2RzJGdlbmVpZCA8LSBnc3ViKCJnZW5lXFwtIiwiIixnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgIlBhcmVudCIpKQpTUzE0LmdmZi5jZHMkbG9jdXNfdGFnIDwtIGdldEF0dHJpYnV0ZUZpZWxkKFNTMTQuZ2ZmLmNkcyRhdHRyaWJ1dGVzLCAibG9jdXNfdGFnIikKU1MxNC5nZmYuY2RzJGdlbmUgPC0gZ2V0QXR0cmlidXRlRmllbGQoU1MxNC5nZmYuY2RzJGF0dHJpYnV0ZXMsICJnZW5lIikKU1MxNC5nZmYuY2RzJHByb2R1Y3QgPC0gZ2V0QXR0cmlidXRlRmllbGQoU1MxNC5nZmYuY2RzJGF0dHJpYnV0ZXMsICJwcm9kdWN0IikKU1MxNC5nZmYuY2RzJHByb3RlaW5pZCA8LSBnZXRBdHRyaWJ1dGVGaWVsZChTUzE0LmdmZi5jZHMkYXR0cmlidXRlcywgInByb3RlaW5faWQiKQojIGNyZWF0ZSBhIG1lcmdlZCBsb2N1c190YWcvZ2VuZSB0aGUgd2F5IHNucEVmZiBkb2VzClNTMTQuZ2ZmLmNkcyRnZW5laWQgPC0gc2FwcGx5KDE6bnJvdyhTUzE0LmdmZi5jZHMpLCBmdW5jdGlvbih4KSBpZmVsc2UoaXMubmEoU1MxNC5nZmYuY2RzJGdlbmVbeF0pLFNTMTQuZ2ZmLmNkcyRsb2N1c190YWdbeF0sIFNTMTQuZ2ZmLmNkcyRnZW5lW3hdKSkKU1MxNC5nZmYuY2RzJGdlbmUuY29vcmRzIDwtIHBhc3RlMChTUzE0LmdmZi5jZHMkc3RhcnQsIjoiLFNTMTQuZ2ZmLmNkcyRlbmQpCgpTUzE0LmdmZi5jZHMKYGBgCgpcCiMgcmVhZCBpbiBzbnAgY2xhc3NpZmljYXRpb25zLCBhbmQgYXBwbHkgdG8gZGlzY3JpbWluYXRvcnkgU05QcwpcCldyaXRlIHRoaXMgYXMgYSBmdW5jdGlvbi4gVGFrZXMgNCBhcmd1bWVudHM6Ci0gZGF0YWZyYW1lIG9mIHNucHMgZm9yIGVhY2ggc2FtcGxlIGluIHdpZGUgbWF0cml4IGZvcm1hdCAoZS5nLiBUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCkKLSBsb25nZm9ybSBsaXN0IG9mIFNOUHMgYW5kIHBvc3NpYmxlIGFsbGVsZXMgKGUuZy4gVFBBLnRyZWV0aW1lLmFuY2VzdHJhbC52Y2YuZml4KQotIHZhcmlhbnQgYW5ub3RhdGlvbnMgZGF0YWZyYW1lIChlLmcuIFRQQS5zbnBFZmYuZmlsdCkKLSBhIHZlY3RvciBvZiB0d28gbm9kZXMgaW4gdGhlIHRyZWUgdG8gY29tcGFyZSAoZS5nLiB0dC5ub2Rlcy50by5jb21wYXJlLlNTMTQpClwKYGBge3J9CmV4dHJhY3RfYnJhbmNoX3NpdGVfYWxsZWxpY19mdW5jdGlvbnMgPC0gZnVuY3Rpb24oYWxsZWxlLm1hdHJpeC5zcHJlYWQsIHNucC50YWJsZSwgc25wLmFubm90YXRpb24udGFibGUsIG5vZGVzLmxpc3QpewogICMgZmlsdGVyIFNOUCBtYXRyaXggdG8gb25seSBpbmNsdWRlIHRoZSB0d28gbm9kZXMgb2YgaW50ZXJlc3QKICBkaXNjcmltaW5hdG9yeS5zaXRlczEgPC0gYWxsZWxlLm1hdHJpeC5zcHJlYWRbYWxsZWxlLm1hdHJpeC5zcHJlYWQkSW5kaXYgJWluJSBub2Rlcy5saXN0LF0KICBkaXNjcmltaW5hdG9yeS5zaXRlczIgPC0gdGlkeXI6OmdhdGhlcihkaXNjcmltaW5hdG9yeS5zaXRlczEsUE9TLEd0LC1JbmRpdikgJT4lIAogIHRpZHlyOjpzcHJlYWQoSW5kaXYsIEd0KQogICMgRmlsdGVyIFNOUHMgdW5kZXIgY29uc2lkZXJhdGlvbiB0byB0aG9zZSB0aGF0IGFyZSBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgdHdvIG5vZGVzCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyIDwtIGRpc2NyaW1pbmF0b3J5LnNpdGVzMlsoZGlzY3JpbWluYXRvcnkuc2l0ZXMyWywyXSE9ZGlzY3JpbWluYXRvcnkuc2l0ZXMyWywzXSksXQogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiA8LSBkaXNjcmltaW5hdG9yeS5zaXRlczJbb3JkZXIoYXMubnVtZXJpYyhkaXNjcmltaW5hdG9yeS5zaXRlczIkUE9TKSksXQogICMgbWVyZ2UgaW4gdGhlIGRldGFpbHMgYWJvdXQgYWxsZWxlcyBhdCBlYWNoIHJlbGV2YW50IFNOUCBwb3NpdGlvbgogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiA8LSBwbHlyOjpqb2luKGRpc2NyaW1pbmF0b3J5LnNpdGVzMiwgc25wLnRhYmxlLGJ5PWMoJ1BPUycpLCB0eXBlPSdsZWZ0JykKICAjIGRlYWwgd2l0aCBtdWx0aS1hbGxlbGljIHNpdGVzLCBhbmQgZGlzY3JpbWluYXRlIGJldHdlZW4gdGhlbQogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiRBTFQubXVsdGkgPC0gZGlzY3JpbWluYXRvcnkuc2l0ZXMyJEFMVAogIGRpc2NyaW1pbmF0b3J5LnNpdGVzMiRBTFQgPC0gc2FwcGx5KDE6bnJvdyhkaXNjcmltaW5hdG9yeS5zaXRlczIpLCBmdW5jdGlvbih4KSBzdHJzcGxpdChkaXNjcmltaW5hdG9yeS5zaXRlczIkQUxULm11bHRpW3hdLCIsIilbWzFdXVtzb3J0KGFzLm51bWVyaWMoKChkaXNjcmltaW5hdG9yeS5zaXRlczJbeCxjKDIsMyldKSkpKVsyXV0pCiAgIyBtZXJnZSBpbiB0aGUgYW5ub3RhdGlvbiBmb3IgdGhlIGFwcHJvcHJpYXRlIGFsbGVsZS9TTlBzCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyLnNucGVmZiA8LSBwbHlyOjpqb2luKHNucC5hbm5vdGF0aW9uLnRhYmxlWyxjKCJQT1MiLCJBTFQiLCJBTk5bKl0uQUxMRUxFIiwiQU5OWypdLkVGRkVDVCIsIkFOTlsqXS5HRU5FIiwiQU5OWypdLkhHVlNfQyIsIkFOTlsqXS5IR1ZTX1AiKV0sIGRpc2NyaW1pbmF0b3J5LnNpdGVzMlssYygiUE9TIiwiUkVGIiwiQUxUIixub2Rlcy5saXN0KV0sIHR5cGU9InJpZ2h0IiwgYnk9YygiUE9TIiwiQUxUIikpCiAgZGlzY3JpbWluYXRvcnkuc2l0ZXMyLnNucGVmZltpcy5uYShkaXNjcmltaW5hdG9yeS5zaXRlczIuc25wZWZmJGBBTk5bKl0uRUZGRUNUYCksIkFOTlsqXS5FRkZFQ1QiXSA8LSAiaW50cmFnZW5pY192YXJpYW50IgogICMgcmV0dXJuIG91dHB1dAogIHJldHVybihkaXNjcmltaW5hdG9yeS5zaXRlczIuc25wZWZmKQp9CmBgYAoKXApgYGB7cn0KI3R0Lm5vZGVzLnRvLmNvbXBhcmUuU1MxNC52cy5OaWNob2xzLlRQQSA8LSBjKCJOT0RFXzAwMDAwMDUiLCJOT0RFXzAwMDAxMDMiKQoKI3R0Lm5vZGVzLnRvLmNvbXBhcmUuc3VibGluZWFnZTYudnMuTVJDQS5UUEEgPC0gYygiTk9ERV8wMDAwMDAzIiwiTk9ERV8wMDAwMDAyIikKdHQubm9kZXMudG8uY29tcGFyZS5zdWJsaW5lYWdlNi52cy5NUkNBLlRQQSA8LSBjKCJOT0RFXzAwMDAwMDEiLCJOT0RFXzAwMDAwMDIiKQoKc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEgPC0gZXh0cmFjdF9icmFuY2hfc2l0ZV9hbGxlbGljX2Z1bmN0aW9ucyhUUEEudHJlZXRpbWUuYW5jZXN0cmFsLnZjZi5ndC5mLnNwcmVhZCxUUEEub25seS5taWRwb2ludC50cmVldGltZS5hbmNlc3RyYWwudmNmLmZpeCxUUEEuc25wRWZmLmZpbHQsIHR0Lm5vZGVzLnRvLmNvbXBhcmUuc3VibGluZWFnZTYudnMuTVJDQS5UUEEpCgpzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSAlPiUgZHBseXI6Omdyb3VwX2J5KGBBTk5bKl0uRUZGRUNUYCkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKcGFzdGUwKCJBbGwgVmFyaWFudHM6ICIsIG5yb3coc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEpKQpwYXN0ZTAoIlVuaXF1ZSBTaXRlczogIiwgbGVuZ3RoKHVuaXF1ZShzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRQT1MpKSkKcGFzdGUwKCJTeW5vbnltb3VzIFZhcmlhbnRzOiAiLCBucm93KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBW3N1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGBBTk5bKl0uRUZGRUNUYD09InN5bm9ueW1vdXNfdmFyaWFudCIsXSkpCnBhc3RlMCgiTm9uLVN5bm9ueW1vdXMgVmFyaWFudHM6ICIsIG5yb3coc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEFbc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkYEFOTlsqXS5FRkZFQ1RgPT0ibWlzc2Vuc2VfdmFyaWFudCIsXSkpCnBhc3RlMCgiSW50cmFnZW5pYyBWYXJpYW50cyA6IiwgbnJvdyhzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQVtzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRgQU5OWypdLkVGRkVDVGA9PSJpbnRyYWdlbmljX3ZhcmlhbnQiLF0pKQoKCgpzdWJsaW42LnZzLm1yY2EuTmljaG9scy5icmFuY2hfc2l0ZV9hbGxlbGVzLlRQQSRkaXN0LmZyb20ubGFzdC52YXIgPC0gYygwLCBzYXBwbHkoMjpucm93KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBKSAsIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkUE9TW3hdKSAtIGFzLm51bWVyaWMoc3VibGluNi52cy5tcmNhLk5pY2hvbHMuYnJhbmNoX3NpdGVfYWxsZWxlcy5UUEEkUE9TW3gtMV0pKSkgCgptZWFuKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWVkaWFuKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWluKHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikKbWF4KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBJGRpc3QuZnJvbS5sYXN0LnZhcikgIAoKcC5zdWJsaW5lYWdlNi5hbmNlc3RyYWwuU05Qcy5nZW5vbWVwb3MgPC0gZ2dwbG90KHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBLCBhZXMoeD1hcy5udW1lcmljKFBPUyksIHk9ZGlzdC5mcm9tLmxhc3QudmFyKSkgKyAKICBnZW9tX3BvaW50KHNpemU9MSwgYWxwaGE9MC41KSArCiAgI2dlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgYWxwaGE9MC41KSArCiAgI2dlb21fbGluZShhbHBoYT0wLjEpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLFNTMTQuYWxuLmxlbmd0aCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXByZXR0eSkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgbGFicyh4PSJTUzE0IEdlbm9tZSBQb3NpdGlvbiAoTkNfMDIxNTA4LjE7IChicCkpIiwgeT0iRGlzdGFuY2Ugb2YgdmFyaWFudCBmcm9tIHByZXZpb3VzIHZhcmlhbnQgc2l0ZSAoYnApIiwgdGl0bGU9Ikdlbm9tZSBwb3NpdGlvbiBvZiBTTlBzIGRlbGluZWF0aW5nIFN1YmxpbmVhZ2UgNiBmcm9tIE1SQ0Egbm9kZSIpCiAgCnAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZ2Vub21lcG9zCgoKCnAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZGlzdC5iZXR3ZWVuLmhpc3RvIDwtIHN1YmxpbjYudnMubXJjYS5OaWNob2xzLmJyYW5jaF9zaXRlX2FsbGVsZXMuVFBBICU+JQogIGdncGxvdChhZXMoeD1kaXN0LmZyb20ubGFzdC52YXIpKSArIAogIHNjYWxlX3hfbG9nMTAoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz01MCkgKyAKICB0aGVtZV9saWdodCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgbGFicyh4PSJEaXN0YW5jZSBvZiB2YXJpYW50IGZyb20gcHJldmlvdXMgdmFyaWFudCBzaXRlIChicCkiLCB5PSJDb3VudCIpICsgY29vcmRfZmxpcCgpCgpwLnN1YmxpbmVhZ2U2LmFuY2VzdHJhbC5TTlBzLmRpc3QuYmV0d2Vlbi5oaXN0bwoKcGxvdF9ncmlkKHAuc3VibGluZWFnZTYuYW5jZXN0cmFsLlNOUHMuZ2Vub21lcG9zLCBwLnN1YmxpbmVhZ2U2LmFuY2VzdHJhbC5TTlBzLmRpc3QuYmV0d2Vlbi5oaXN0byArIHkudGhlbWUuc3RyaXAgLCByZWxfd2lkdGhzID0gYyg4LDEpLCBhbGlnbiA9IFQpCmBgYApcClwKRG8gc29tZSBmdXJ0aGVyIGFuYWx5c2lzIG9mIHRoZSBOb3J0aCBFYXN0IHN1YmxpbmVhZ2UgZGlzdHJpYnV0aW9ucy4gV2UgaGF2ZSAzNSBzYW1wbGVzIGNvbGxlY3RlZCBmcm9tIHRoZXNlIHJlZ2lvbnMsIG9mIHdoaWNoIDE3IHdlcmUgY29sbGVjdGVkIGZyb20gMjAxNCBvbndhcmRzLiBJcyBzdWJsaW5lYWdlIDE0IG1pc3NpbmcgYnkgY2hhbmNlIChjb3VsZCB3ZSBiZSBtaXNzaW5nIGl0IHNpbXBseSBiZWNhdXNlIHdlIGhhdmVuJ3QgY29sbGVjdGVkIGVub3VnaCBzYW1wbGVzKSBvciBpcyB0aGlzIG1vcmUgbGlrZWx5IHRvIHJlZmxlY3QgdHJ1ZSB1bmV2ZW4gcmVnaW9uYWwgZGlzdHJpYnV0aW9ucz8KYGBge3J9CiMgSG93IG1hbnkgZ2Vub21lcyBmb3VuZCBpbiBOb3J0aGVybiByZWdpb25zIGJlZm9yZSBhbmQgYWZ0ZXIgZmlyc3QgZGV0ZWN0aW9uIG9mIHN1YmxpbmVhZ2UgMTQgaW4gMjAxND8KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgZHBseXI6Om11dGF0ZShiZWZvcmUyMDE0PWlmZWxzZSh5ZWFyPj0yMDE0LCIyMDE0b253YXJkcyIsICJwcmUyMDE0IikpICU+JQogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoYmVmb3JlMjAxNCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudD1uKCkpCgojIFdoYXQgYXJlIHRoZSBwcm9wb3J0aW9ucyBvZiBkaWZmZXJlbnQgc3VibGluZWFnZXMgYXJvdW5kIHRoZSBVSyBiZWZvcmUgYW5kIGFmdGVyIDIwMTQ/ClBIRS5tZXRhLnBvc3QyMDE0LnN1Ymxpbi5mcmFjcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICAjZHBseXI6OmZpbHRlcih5ZWFyPj0yMDE0KSAlPiUKICBkcGx5cjo6bXV0YXRlKGJlZm9yZTIwMTQ9aWZlbHNlKHllYXI+PTIwMTQsIjIwMTRvbndhcmRzIiwgInByZTIwMTQiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGJlZm9yZTIwMTQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5hbGw9c3VtKENvdW50KSkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5hbGwpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExpbmVhZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQpQSEUubWV0YS5wb3N0MjAxNC5zdWJsaW4uZnJhY3MgCgoKIyBzaW11bGF0aW5nIHBvaXNzb24gcHJvY2VzcyByIHRvIHdvcmsgb3V0IGhvdyBtYW55IHNhbXBsZXMgd2Ugd291bGQgZXhwZWN0IGluIE5vcnRoZXJuIEVuZ2xhbmQgdW5kZXIgcG9pc3NvbiBkaXN0cmlidXRpb24KCiMgV2hhdCAlIG9mIHN1YmxpbmVhZ2UgMTQgc2FtcGxlcyBhcmUgZm91bmQgaW4gdGhlIHRvdGFsIHBvcHVsYXRpb24/CnBvc3QyMDE0LnN1YmxpbjE0LmZyZXEgPC0gUEhFLm1ldGEucG9zdDIwMTQuc3VibGluLmZyYWNzICU+JSBmaWx0ZXIoYmVmb3JlMjAxND09IjIwMTRvbndhcmRzIiwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PTE0KSAlPiUgc2VsZWN0KExpbmVhZ2UucGVyYykgJT4lIHB1bGwoKQogCgojIFNpbXVsYXRlIGFuZCBwbG90IGEgUG9pc3NvbiBkaXN0cmlidXRpb24gb2YgaG93IG1hbnkgc3VibGluZWFnZSAxNCBzYW1wbGVzIHdlIHdvdWxkIGV4cGVjdCB0byBmaW5kIGlmIHdlIHJhbmRvbWx5IHNlbGVjdGVkIDE3IHNhbXBsZXMgYXQgMjIlIApkYXRhLmZyYW1lKHJwb2lzPXJwb2lzKDEwMDAwMDAsIDE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpKSAlPiUKICBnZ3Bsb3QoYWVzKHJwb2lzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwyMCwyKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIGxhYnMoeD0iU2FtcGxlcyBGb3VuZCIsIHk9IlNpbXVsYXRpb24gQ291bnQiKQoKIyBXaGF0IGFyZSB0aGUgcXVhbnRpbGUgZGlzdHJpYnV0aW9ucyBmcm9tIHRoYXQ/CnF1YW50aWxlKHJwb2lzKDEwMDAwMDAsIDE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpLCBwcm9icz1jKDAuMDEsIDAuMDUsIDAuNSwgMC45NSwgMC45OSkpCm1lZGlhbihycG9pcygxMDAwMDAwLCAxNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkKbWVhbihycG9pcygxMDAwMDAwLCAxNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkKCiMgV2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgb2YgZmluZGluZyBubyBzYW1wbGVzIChhc3N1bWluZyB1bmlmb3JtIHVuYmlhc2VkIGNvdmVyYWdlKT8KZGF0YS5mcmFtZShuPXNlcSgwLDIwLDEpLCBkcG9pcz1zYXBwbHkoc2VxKDAsMjAsMSksIGZ1bmN0aW9uKHgpIGRwb2lzKHgsIGxhbWJkYT0xNy8oMTAwL3Bvc3QyMDE0LnN1YmxpbjE0LmZyZXEpKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9biwgeT1kcG9pcykpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1wcmV0dHkpICsKICB0aGVtZV9saWdodCgpICsKICBsYWJzKHg9IlNhbXBsZXMgRm91bmQiLCB5PSJQcm9iYWJpbGl0eSIpCgpwYXN0ZSgiUHJvYmFiaWxpdHkgb2YgZmluZGluZyB6ZXJvIHNhbXBsZXMgaXMgIiwgcm91bmQoZHBvaXMoMCwgbGFtYmRhPTE3LygxMDAvcG9zdDIwMTQuc3VibGluMTQuZnJlcSkpLCA1KSkgCmBgYAoKXApTZXB0ZW1iZXIgMjAyMyAtIFB1bGwgb3V0IHNvbWUgYWRkaXRpb25hbCBzdGF0aXN0aWNzL3BlcmNlbnRhZ2VzIHJlcXVlc3RlZCBieSBzdWJlZGl0b3IgZm9yIGZpbmFsIG1hbnVzY3JpcHQgcHJvb2ZzCmBgYHtyfQojIENvdW50cyBhbmQgJSBvZiBlYWNoIGdlbmRlcgpQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6bXV0YXRlKEdlbmRlcj1pZmVsc2UoZ2VuZGVyX29yaWVudGF0aW9uICVpbiUgYygiR0JNU00iLCAiTVVua25vd24iLCAiTVNXIiksICJNYWxlIiwgaWZlbHNlKGdlbmRlcl9vcmllbnRhdGlvbiAlaW4lIGMoIldTTSIsICJXU1ciKSwgIkZlbWFsZSIsICJVbmtub3duIikpKSAlPiUgCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zYW1wbGVzPW4oKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlbmRlcikgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoR2VuZGVyLkNvdW50PW4oKSwgR2VuZGVyLlBlcmM9KEdlbmRlci5Db3VudC8yMzcpKjEwMCkKCgojIEV4YWN0IGRhdGVzIG9mIHNhbXBsaW5nIGZyYW1lCmRlY2ltYWwyRGF0ZShtYXgoUEhFLm1ldGFkYXRhLmxpbmtlZCRkYXRlLmRlY2ltYWwpKSAjIGxhc3Qgc2FtcGxlIChyZXZpc2UgdG8gZW5kIG9mIG1vbnRoKQpkZWNpbWFsMkRhdGUobWluKFBIRS5tZXRhZGF0YS5saW5rZWQkZGF0ZS5kZWNpbWFsKSkgIyBmaXJzdCBzYW1wbGUgKHJldmlzZSB0byBzdGFydCBvZiBtb250aCkKIyBXaGVyZSBkaWQgdGhvc2UgbGFzdCBzYW1wbGVzIGNvbWUgZnJvbSAtIGFyZSB0aGV5IG5vbi1QSEUsIGFuZCB3aGVuIHdhcyB0aGUgbGFzdCBVS0hTQSBzYW1wbGU/ClBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIHNlbGVjdChTYW1wbGVfTmFtZSwgZGF0ZS5kZWNpbWFsKSAlPiUKICBhcnJhbmdlKGRhdGUuZGVjaW1hbCkgCgoKIyBDb3VudHMgYW5kICUgb2YgaGV0ZXJvc2V4dWFscyAmIEdCTVNNIGluIFVLSFNBIGRhdGFzZXQgKGFzIG9wcG9zZWQgdG8gY29tYmluZWQgVUtIU0EgKyBwcm9zcGVjdGl2ZSkKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihpcy5QSEU9PSJQSEUiKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkKCmBgYAoKCg==